diff --git a/HISTORY b/HISTORY index c9ac4eb..4cc8caa 100755 --- a/HISTORY +++ b/HISTORY @@ -1,4 +1,5 @@ Jan. 18 2016 +Added variable arguments support Added a NOW statement to the shell Polished shell implementation code diff --git a/core/my_basic.c b/core/my_basic.c index 85648a5..393d7c4 100755 --- a/core/my_basic.c +++ b/core/my_basic.c @@ -132,6 +132,8 @@ extern "C" { #define DON(__o) ((__o) ? ((_object_t*)((__o)->data)) : 0) #define TON(__t) (((__t) && *(__t)) ? ((_object_t*)(((_tuple3_t*)(*(__t)))->e1)) : 0) +#define _IS_VAR_ARGS(__v) ((__v) == &_VAR_ARGS) + #define _IS_EOS(__o) (__o && ((_object_t*)(__o))->type == _DT_EOS) #define _IS_SEP(__o, __c) (((_object_t*)(__o))->type == _DT_SEP && ((_object_t*)(__o))->data.separator == __c) #define _IS_FUNC(__o, __f) (((_object_t*)(__o))->type == _DT_FUNC && ((_object_t*)(__o))->data.func->pointer == __f) @@ -599,6 +601,12 @@ static const _object_t _OBJ_INT_ZERO = { _DT_INT, (mb_data_e)0, false, 0 }; static _object_t* _OBJ_BOOL_TRUE = 0; static _object_t* _OBJ_BOOL_FALSE = 0; +#ifdef MB_ENABLE_CLASS +static const _var_t _VAR_ARGS = { "...", 0, 0 }; +#else /* MB_ENABLE_CLASS */ +static const _var_t _VAR_ARGS = { "...", 0 }; +#endif /* MB_ENABLE_CLASS */ + /* Parsing context */ #define _CLASS_STATE_NONE 0 #define _CLASS_STATE_PROC 1 @@ -677,6 +685,7 @@ typedef struct mb_interpreter_t { _ls_node_t* ast; _parsing_context_t* parsing_context; _running_context_t* running_context; + _ls_node_t* var_args; unsigned char jump_set; #ifdef MB_ENABLE_CLASS _class_t* last_instance; @@ -978,6 +987,8 @@ static _ls_node_t* _ls_find(_ls_node_t* list, void* data, _ls_compare cmp); static _ls_node_t* _ls_back(_ls_node_t* node); static _ls_node_t* _ls_pushback(_ls_node_t* list, void* data); static void* _ls_popback(_ls_node_t* list); +static _ls_node_t* _ls_front(_ls_node_t* node); +static void* _ls_popfront(_ls_node_t* list); static _ls_node_t* _ls_insert_at(_ls_node_t* list, int index, void* data); static unsigned int _ls_remove(_ls_node_t* list, _ls_node_t* node, _ls_operation op); static unsigned int _ls_try_remove(_ls_node_t* list, void* info, _ls_compare cmp, _ls_operation op); @@ -1091,7 +1102,10 @@ static int _get_priority_index(mb_func_t op); static _object_t* _operate_operand(mb_interpreter_t* s, _object_t* optr, _object_t* opnd1, _object_t* opnd2, int* status); static bool_t _is_expression_terminal(mb_interpreter_t* s, _object_t* obj); static int _calc_expression(mb_interpreter_t* s, _ls_node_t** l, _object_t** val); -static int _proc_args(mb_interpreter_t* s, _ls_node_t** l, _running_context_t* running, mb_value_t* va, unsigned ca, _routine_t* r, mb_has_routine_arg_func_t has_arg, mb_pop_routine_arg_func_t pop_arg, bool_t proc_ref); +static _ls_node_t* _push_var_args(mb_interpreter_t* s); +static void _pop_var_args(mb_interpreter_t* s, _ls_node_t* last_var_args); +static int _pop_arg(mb_interpreter_t* s, _ls_node_t** l, mb_value_t* va, unsigned ca, unsigned* ia, _routine_t* r, mb_pop_routine_arg_func_t pop_arg, _ls_node_t* args, mb_value_t* arg); +static int _proc_args(mb_interpreter_t* s, _ls_node_t** l, _running_context_t* running, mb_value_t* va, unsigned ca, _routine_t* r, mb_has_routine_arg_func_t has_arg, mb_pop_routine_arg_func_t pop_arg, bool_t proc_ref, _ls_node_t* args); static int _eval_routine(mb_interpreter_t* s, _ls_node_t** l, mb_value_t* va, unsigned ca, _routine_t* r, mb_has_routine_arg_func_t has_arg, mb_pop_routine_arg_func_t pop_arg); static int _eval_script_routine(mb_interpreter_t* s, _ls_node_t** l, mb_value_t* va, unsigned ca, _routine_t* r, mb_has_routine_arg_func_t has_arg, mb_pop_routine_arg_func_t pop_arg); #ifdef MB_ENABLE_LAMBDA @@ -1646,6 +1660,7 @@ static int _core_return(mb_interpreter_t* s, void** l); static int _core_call(mb_interpreter_t* s, void** l); static int _core_def(mb_interpreter_t* s, void** l); static int _core_enddef(mb_interpreter_t* s, void** l); +static int _core_args(mb_interpreter_t* s, void** l); #ifdef MB_ENABLE_CLASS static int _core_class(mb_interpreter_t* s, void** l); static int _core_endclass(mb_interpreter_t* s, void** l); @@ -1763,6 +1778,7 @@ static const _func_t _core_libs[] = { { "CALL", _core_call }, { "DEF", _core_def }, { "ENDDEF", _core_enddef }, + { "...", _core_args }, #ifdef MB_ENABLE_CLASS { "CLASS", _core_class }, @@ -1964,6 +1980,35 @@ void* _ls_popback(_ls_node_t* list) { return result; } +_ls_node_t* _ls_front(_ls_node_t* node) { + _ls_node_t* result = node; + + result = result->next; + + return result; +} + +void* _ls_popfront(_ls_node_t* list) { + void* result = 0; + _ls_node_t* tmp = 0; + + mb_assert(list); + + tmp = _ls_front(list); + if(tmp) { + result = tmp->data; + list->next = tmp->next; + if(tmp->next) + tmp->next->prev = list; + if(!list->next) + list->prev = 0; + tmp->prev = tmp->next = 0; + safe_free(tmp); + } + + return result; +} + _ls_node_t* _ls_insert_at(_ls_node_t* list, int index, void* data) { _ls_node_t* result = 0; _ls_node_t* tmp = 0; @@ -3248,7 +3293,46 @@ _exit: return result; } -int _proc_args(mb_interpreter_t* s, _ls_node_t** l, _running_context_t* running, mb_value_t* va, unsigned ca, _routine_t* r, mb_has_routine_arg_func_t has_arg, mb_pop_routine_arg_func_t pop_arg, bool_t proc_ref) { +_ls_node_t* _push_var_args(mb_interpreter_t* s) { + /* Push current variable arguments list */ + _ls_node_t* result = s->var_args; + s->var_args = 0; + + return result; +} + +void _pop_var_args(mb_interpreter_t* s, _ls_node_t* last_var_args) { + /* Pop current variable arguments list */ + _ls_node_t* var_args = s->var_args; + s->var_args = last_var_args; + if(var_args) { + _ls_foreach(var_args, _destroy_object_capsule_only); + _ls_destroy(var_args); + } +} + +int _pop_arg(mb_interpreter_t* s, _ls_node_t** l, mb_value_t* va, unsigned ca, unsigned* ia, _routine_t* r, mb_pop_routine_arg_func_t pop_arg, _ls_node_t* args, mb_value_t* arg) { + /* Pop an argument from the caller or a variable argument list */ + int result = MB_FUNC_OK; + _ls_node_t* ast = *l; + + mb_make_nil(*arg); + if(ast && _IS_FUNC(ast->data, _core_args)) { + if(args) { + _object_t* obj = (_object_t*)_ls_popfront(args); + if(obj) { + _internal_object_to_public_value(obj, arg); + _destroy_object_capsule_only(obj, 0); + } + } + } else { + result = pop_arg(s, (void**)l, va, ca, ia, r, arg); + } + + return result; +} + +int _proc_args(mb_interpreter_t* s, _ls_node_t** l, _running_context_t* running, mb_value_t* va, unsigned ca, _routine_t* r, mb_has_routine_arg_func_t has_arg, mb_pop_routine_arg_func_t pop_arg, bool_t proc_ref, _ls_node_t* args) { /* Process arguments of a routine */ int result = MB_FUNC_OK; _ls_node_t* parameters = 0; @@ -3257,6 +3341,7 @@ int _proc_args(mb_interpreter_t* s, _ls_node_t** l, _running_context_t* running, _var_t* var = 0; _ls_node_t* rnode = 0; unsigned ia = 0; + _ls_node_t* var_args = 0; parameters = r->func.basic.parameters; #ifdef MB_ENABLE_LAMBDA @@ -3269,16 +3354,17 @@ int _proc_args(mb_interpreter_t* s, _ls_node_t** l, _running_context_t* running, pars = parameters; pars = pars->next; while(pars && (!has_arg || (has_arg && has_arg(s, (void**)l, va, ca, &ia, r)))) { - _object_t* obj = 0; - if(pop_arg) { - mb_make_nil(arg); - mb_check(pop_arg(s, (void**)l, va, ca, &ia, r, &arg)); - } - var = (_var_t*)pars->data; pars = pars->next; + if(_IS_VAR_ARGS(var)) + break; + + if(pop_arg) { + mb_check(_pop_arg(s, l, va, ca, &ia, r, pop_arg, args, &arg)); + } + if(running->meta == _SCOPE_META_REF) { - obj = (_object_t*)(_ht_find(running->var_dict, var->name)->data); + _object_t* obj = (_object_t*)(_ht_find(running->var_dict, var->name)->data); var = obj->data.variable; if(proc_ref) @@ -3293,8 +3379,38 @@ int _proc_args(mb_interpreter_t* s, _ls_node_t** l, _running_context_t* running, } result = _public_value_to_internal_object(&arg, var->data); + if(result != MB_FUNC_OK) break; + + if(args && _ls_empty(args)) + break; + } + if(_IS_VAR_ARGS(var)) { + if(has_arg && !var_args && _IS_VAR_ARGS(var)) + var_args = s->var_args = _ls_create(); + + while(has_arg && has_arg(s, (void**)l, va, ca, &ia, r)) { + if(pop_arg) { + mb_check(_pop_arg(s, l, va, ca, &ia, r, pop_arg, args, &arg)); + } + + if(var_args) { + _object_t* obj = _create_object(); + result = _public_value_to_internal_object(&arg, obj); + _ls_pushback(var_args, obj); + } + + if(args && _ls_empty(args)) + break; + } + } + if(_IS_VAR_ARGS(var)) { + if(args) { + _ls_node_t* ast = *l; + if(ast) ast = ast->next; + *l = ast; + } } } @@ -3336,6 +3452,7 @@ int _eval_script_routine(mb_interpreter_t* s, _ls_node_t** l, mb_value_t* va, un _running_context_t* running = 0; _routine_t* lastr = 0; mb_value_t inte; + _ls_node_t* lastv = 0; mb_assert(s && l && r); @@ -3356,12 +3473,14 @@ int _eval_script_routine(mb_interpreter_t* s, _ls_node_t** l, mb_value_t* va, un lastr = s->last_routine; s->last_routine = r; + lastv = _push_var_args(s); + if(!va) { mb_check(mb_attempt_open_bracket(s, (void**)l)); } running = _push_weak_scope_by_routine(s, r->func.basic.scope, r); - result = _proc_args(s, l, running, va, ca, r, has_arg, pop_arg, true); + result = _proc_args(s, l, running, va, ca, r, has_arg, pop_arg, true, lastv); if(result != MB_FUNC_OK) { if(running->meta == _SCOPE_META_REF) _destroy_scope(s, running); @@ -3373,7 +3492,7 @@ int _eval_script_routine(mb_interpreter_t* s, _ls_node_t** l, mb_value_t* va, un running = _pop_weak_scope(s, running); if(!va) { - mb_check(mb_attempt_close_bracket(s, (void**)l)); + _mb_check_mark(mb_attempt_close_bracket(s, (void**)l), result, _error); } ast = (_ls_node_t*)*l; @@ -3414,7 +3533,7 @@ int _eval_script_routine(mb_interpreter_t* s, _ls_node_t** l, mb_value_t* va, un _out_of_scope(s, running, 0, true); #endif /* MB_ENABLE_CLASS */ - result = _proc_args(s, l, running, 0, 0, r, 0, 0, false); + result = _proc_args(s, l, running, 0, 0, r, 0, 0, false, 0); if(result != MB_FUNC_OK) goto _exit; @@ -3430,8 +3549,11 @@ _exit: if(result != MB_FUNC_OK) _pop_scope(s, true); +_error: s->last_routine = lastr; + _pop_var_args(s, lastv); + _tail: return result; } @@ -3444,18 +3566,21 @@ int _eval_lambda_routine(mb_interpreter_t* s, _ls_node_t** l, mb_value_t* va, un _running_context_t* running = 0; _routine_t* lastr = 0; mb_value_t inte; + _ls_node_t* lastv = 0; mb_assert(s && l && r); lastr = s->last_routine; s->last_routine = r; + lastv = _push_var_args(s); + if(!va) { mb_check(mb_attempt_open_bracket(s, (void**)l)); } running = _link_lambda_scope_chain(s, &r->func.lambda, true); - result = _proc_args(s, l, running, va, ca, r, has_arg, pop_arg, true); + result = _proc_args(s, l, running, va, ca, r, has_arg, pop_arg, true, lastv); if(result != MB_FUNC_OK) { _unlink_lambda_scope_chain(s, &r->func.lambda, true); @@ -3464,7 +3589,7 @@ int _eval_lambda_routine(mb_interpreter_t* s, _ls_node_t** l, mb_value_t* va, un running = _unlink_lambda_scope_chain(s, &r->func.lambda, true); if(!va) { - mb_check(mb_attempt_close_bracket(s, (void**)l)); + _mb_check_mark(mb_attempt_close_bracket(s, (void**)l), result, _error); } ast = (_ls_node_t*)*l; @@ -3501,7 +3626,7 @@ int _eval_lambda_routine(mb_interpreter_t* s, _ls_node_t** l, mb_value_t* va, un _out_of_scope(s, running, 0, true); - result = _proc_args(s, l, running, 0, 0, r, 0, 0, false); + result = _proc_args(s, l, running, 0, 0, r, 0, 0, false, 0); if(result != MB_FUNC_OK) goto _exit; @@ -3517,8 +3642,11 @@ _exit: if(result != MB_FUNC_OK) _unlink_lambda_scope_chain(s, &r->func.lambda, false); +_error: s->last_routine = lastr; + _pop_var_args(s, lastv); + *l = ast; return result; @@ -3530,12 +3658,15 @@ int _eval_native_routine(mb_interpreter_t* s, _ls_node_t** l, mb_value_t* va, un int result = MB_FUNC_OK; _routine_t* lastr = 0; mb_routine_func_t entry = 0; + _ls_node_t* lastv = 0; mb_assert(s && l && r); lastr = s->last_routine; s->last_routine = r; + lastv = _push_var_args(s); + entry = r->func.native.entry; if(!entry) { _handle_error_on_obj(s, SE_RN_INVALID_ROUTINE, s->source_file, TON(l), MB_FUNC_ERR, _exit, result); @@ -3546,6 +3677,8 @@ int _eval_native_routine(mb_interpreter_t* s, _ls_node_t** l, mb_value_t* va, un _exit: s->last_routine = lastr; + _pop_var_args(s, lastv); + return result; } @@ -10357,6 +10490,8 @@ int mb_run(struct mb_interpreter_t* s) { _destroy_parsing_context(&s->parsing_context); + s->source_file = 0; + s->handled_error = false; if(s->suspent_point) { @@ -12175,6 +12310,10 @@ int _core_def(mb_interpreter_t* s, void** l) { if(!routine->func.basic.parameters) routine->func.basic.parameters = _ls_create(); _ls_pushback(routine->func.basic.parameters, var); + } else if(_IS_FUNC(obj, _core_args)) { + if(!routine->func.basic.parameters) + routine->func.basic.parameters = _ls_create(); + _ls_pushback(routine->func.basic.parameters, (void*)&_VAR_ARGS); } ast = ast->next; @@ -12213,6 +12352,42 @@ _exit: return result; } +int _core_args(mb_interpreter_t* s, void** l) { + /* ... (variable argument list) statement */ + int result = MB_FUNC_OK; + _ls_node_t* ast = 0; + _ls_node_t* var_args = 0; + bool_t pushed = false; + + mb_assert(s && l); + + ast = (_ls_node_t*)*l; + if(ast) ast = ast->next; + *l = ast; + + var_args = s->var_args; + if(var_args) { + _object_t* obj = (_object_t*)_ls_popfront(var_args); + if(obj) { + mb_value_t arg; + mb_make_nil(arg); + _internal_object_to_public_value(obj, &arg); + mb_check(mb_push_value(s, l, arg)); + pushed = true; + _destroy_object_capsule_only(obj, 0); + } + } + + if(!pushed) { + mb_value_t arg; + mb_make_nil(arg); + arg.type = MB_DT_UNKNOWN; + mb_check(mb_push_value(s, l, arg)); + } + + return result; +} + #ifdef MB_ENABLE_CLASS int _core_class(mb_interpreter_t* s, void** l) { /* CLASS statement */ diff --git a/output/my_basic.exe b/output/my_basic.exe index 2046b2f..b0327d4 100755 Binary files a/output/my_basic.exe and b/output/my_basic.exe differ diff --git a/shell/main.c b/shell/main.c index 388a7b7..51310de 100755 --- a/shell/main.c +++ b/shell/main.c @@ -870,7 +870,7 @@ static int _do_line(void) { result = _new_program(); } else if(_str_eq(line, "RUN")) { int i = 0; - mb_assert(_get_code()); + mb_assert(_code()); result = mb_reset(&bas, false); for(i = 0; i < _code()->count; ++i) { if(result)