diff --git a/ChangeLog b/ChangeLog index 6d857ee3..7eb7e802 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,6 @@ +2024-01-06 (12.27) + COMMON: Allow modules to return objects with methods that take arguments + 2023-08-27 (12.27) COMMON: Fix parameter number error when calling a unit sub/func COMMON: Show a runtime error if a module calls exit() diff --git a/src/common/blib.c b/src/common/blib.c index 6902ab94..d3e10fcc 100644 --- a/src/common/blib.c +++ b/src/common/blib.c @@ -2881,13 +2881,10 @@ void cmd_call_vfunc() { rt_raise(ERR_NO_FUNC); } } else { - if (code_peek() == kwTYPE_LEVEL_BEGIN) { - code_skipnext(); - } - v_func->v.fn.cb(map, NULL); - if (code_peek() == kwTYPE_LEVEL_END) { - code_skipnext(); - } + var_t result; + v_init(&result); + v_eval_func(map, v_func, &result); + v_free(&result); } } diff --git a/src/common/plugins.c b/src/common/plugins.c index 0e5159b3..cd5c4b5a 100644 --- a/src/common/plugins.c +++ b/src/common/plugins.c @@ -358,80 +358,6 @@ static void slib_import_routines(slib_t *lib, int comp) { } } -// -// build parameter table -// -static int slib_build_ptable(slib_par_t *ptable) { - int pcount = 0; - var_t *arg; - bcip_t ofs; - - if (code_peek() == kwTYPE_LEVEL_BEGIN) { - code_skipnext(); - byte ready = 0; - do { - byte code = code_peek(); - switch (code) { - case kwTYPE_EOC: - code_skipnext(); - break; - case kwTYPE_SEP: - code_skipsep(); - break; - case kwTYPE_LEVEL_END: - ready = 1; - break; - case kwTYPE_VAR: - // variable - ofs = prog_ip; - if (code_isvar()) { - // push parameter - ptable[pcount].var_p = code_getvarptr(); - ptable[pcount].byref = 1; - pcount++; - break; - } - - // restore IP - prog_ip = ofs; - // no 'break' here - default: - // default --- expression (BYVAL ONLY) - arg = v_new(); - eval(arg); - if (!prog_error) { - // push parameter - ptable[pcount].var_p = arg; - ptable[pcount].byref = 0; - pcount++; - } else { - v_free(arg); - v_detach(arg); - return pcount; - } - } - if (pcount == MAX_PARAM) { - err_parm_limit(MAX_PARAM); - } - } while (!ready && !prog_error); - // kwTYPE_LEVEL_END - code_skipnext(); - } - return pcount; -} - -// -// free parameter table -// -static void slib_free_ptable(slib_par_t *ptable, int pcount) { - for (int i = 0; i < pcount; i++) { - if (ptable[i].byref == 0) { - v_free(ptable[i].var_p); - v_detach(ptable[i].var_p); - } - } -} - // // execute a function or procedure // @@ -440,13 +366,13 @@ static int slib_exec(slib_t *lib, var_t *ret, int index, int proc) { int pcount; if (code_peek() == kwTYPE_LEVEL_BEGIN) { ptable = (slib_par_t *)malloc(sizeof(slib_par_t) * MAX_PARAM); - pcount = slib_build_ptable(ptable); + pcount = plugin_build_ptable(ptable, MAX_PARAM); } else { ptable = NULL; pcount = 0; } if (prog_error) { - slib_free_ptable(ptable, pcount); + plugin_free_ptable(ptable, pcount); free(ptable); return 0; } @@ -470,7 +396,7 @@ static int slib_exec(slib_t *lib, var_t *ret, int index, int proc) { // clean-up if (ptable) { - slib_free_ptable(ptable, pcount); + plugin_free_ptable(ptable, pcount); free(ptable); } @@ -643,3 +569,71 @@ int plugin_funcexec(int lib_id, int index, var_t *ret) { return -1; } void plugin_free(int lib_id, int cls_id, int id) {} void plugin_close() {} #endif + +int plugin_build_ptable(slib_par_t *ptable, int size) { + int pcount = 0; + var_t *arg; + bcip_t ofs; + + if (code_peek() == kwTYPE_LEVEL_BEGIN) { + code_skipnext(); + byte ready = 0; + do { + byte code = code_peek(); + switch (code) { + case kwTYPE_EOC: + code_skipnext(); + break; + case kwTYPE_SEP: + code_skipsep(); + break; + case kwTYPE_LEVEL_END: + ready = 1; + break; + case kwTYPE_VAR: + // variable + ofs = prog_ip; + if (code_isvar()) { + // push parameter + ptable[pcount].var_p = code_getvarptr(); + ptable[pcount].byref = 1; + pcount++; + break; + } + + // restore IP + prog_ip = ofs; + // no 'break' here + default: + // default --- expression (BYVAL ONLY) + arg = v_new(); + eval(arg); + if (!prog_error) { + // push parameter + ptable[pcount].var_p = arg; + ptable[pcount].byref = 0; + pcount++; + } else { + v_free(arg); + v_detach(arg); + return pcount; + } + } + if (pcount == size) { + err_parm_limit(size); + } + } while (!ready && !prog_error); + // kwTYPE_LEVEL_END + code_skipnext(); + } + return pcount; +} + +void plugin_free_ptable(slib_par_t *ptable, int pcount) { + for (int i = 0; i < pcount; i++) { + if (ptable[i].byref == 0) { + v_free(ptable[i].var_p); + v_detach(ptable[i].var_p); + } + } +} diff --git a/src/common/plugins.h b/src/common/plugins.h index a4dd16bd..ceac683c 100644 --- a/src/common/plugins.h +++ b/src/common/plugins.h @@ -44,7 +44,7 @@ int plugin_get_kid(int lib_id, const char *keyword); // returns the function pointer for the given function name // void *plugin_get_func(const char *name); - + // // executes the plugin procedure at the given index // @@ -65,6 +65,16 @@ void plugin_free(int lib_id, int cls_id, int id); // void plugin_close(); +// +// build parameter table +// +int plugin_build_ptable(slib_par_t *ptable, int size); + +// +// free parameter table +// +void plugin_free_ptable(slib_par_t *ptable, int pcount); + #if defined(__cplusplus) } #endif diff --git a/src/common/var.c b/src/common/var.c index 05017773..6f46ca7b 100644 --- a/src/common/var.c +++ b/src/common/var.c @@ -800,5 +800,14 @@ void v_create_func(var_p_t map, const char *name, method cb) { var_p_t v_func = map_add_var(map, name, 0); v_func->type = V_FUNC; v_func->v.fn.cb = cb; + v_func->v.fn.mcb = NULL; + v_func->v.fn.id = 0; +} + +void v_create_callback(var_p_t map, const char *name, callback cb) { + var_p_t v_func = map_add_var(map, name, 0); + v_func->type = V_FUNC; + v_func->v.fn.cb = NULL; + v_func->v.fn.mcb = cb; v_func->v.fn.id = 0; } diff --git a/src/common/var_eval.c b/src/common/var_eval.c index 4509fef9..7bcec5de 100644 --- a/src/common/var_eval.c +++ b/src/common/var_eval.c @@ -5,7 +5,7 @@ // This program is distributed under the terms of the GPL v2.0 or later // Download the GNU Public License (GPL) from www.gnu.org // -// Copyright(C) 2010-2014 Chris Warren-Smith. [http://tinyurl.com/ja2ss] +// Copyright(C) 2010-2024 Chris Warren-Smith. [http://tinyurl.com/ja2ss] #include "common/sys.h" #include "common/kw.h" @@ -14,6 +14,55 @@ #include "common/smbas.h" #include "common/var.h" #include "common/var_eval.h" +#include "common/plugins.h" + +// +// returns a temporary var that can exist in the calling scope +// +var_p_t v_get_tmp(var_p_t map) { + var_p_t result = map_get(map, MAP_TMP_FIELD); + if (result == NULL) { + result = map_add_var(map, MAP_TMP_FIELD, 0); + } + return result; +} + +void v_eval_func(var_p_t self, var_p_t v_func, var_p_t result) { + if (v_func->v.fn.cb != NULL) { + // internal object method + if (code_peek() == kwTYPE_LEVEL_BEGIN) { + code_skipnext(); + } + v_func->v.fn.cb(self, NULL); + if (code_peek() == kwTYPE_LEVEL_END) { + code_skipnext(); + } + } else { + // module callback + slib_par_t *ptable; + int pcount; + if (code_peek() == kwTYPE_LEVEL_BEGIN) { + ptable = (slib_par_t *)malloc(sizeof(slib_par_t) * MAX_PARAMS); + pcount = plugin_build_ptable(ptable, MAX_PARAMS); + } else { + ptable = NULL; + pcount = 0; + } + if (!prog_error) { + if (!v_func->v.fn.mcb(self, pcount, ptable, result)) { + if (result->type == V_STR) { + err_throw(result->v.p.ptr); + } else { + err_throw("Undefined"); + } + } + } + if (ptable) { + plugin_free_ptable(ptable, pcount); + free(ptable); + } + } +} /** * Convert multi-dim index to one-dim index @@ -147,11 +196,7 @@ var_t *code_get_map_element(var_t *map, var_t *field) { if (udf_rv.type != kwTYPE_RET) { err_stackmess(); } else { - // result must exist until processed in eval() - var_p_t var = map_get(map, MAP_TMP_FIELD); - if (var == NULL) { - var = map_add_var(map, MAP_TMP_FIELD, 0); - } + var_p_t var = v_get_tmp(map); v_move(var, udf_rv.x.vdvar.vptr); v_detach(udf_rv.x.vdvar.vptr); result = var; @@ -159,6 +204,10 @@ var_t *code_get_map_element(var_t *map, var_t *field) { } } else if (field->type == V_ARRAY) { result = code_getvarptr_arridx(field); + } else if (field->type == V_FUNC) { + result = v_get_tmp(map); + v_init(result); + v_eval_func(map, field, result); } else { code_skipnext(); var_t var; diff --git a/src/common/var_eval.h b/src/common/var_eval.h index 9ba681ef..6daee18a 100644 --- a/src/common/var_eval.h +++ b/src/common/var_eval.h @@ -67,6 +67,13 @@ int code_isvar(void); */ void v_eval_str(var_p_t v); +/** + * @ingroup var + * + * invokes the virtual function + */ +void v_eval_func(var_p_t self, var_p_t v_func, var_p_t result); + #if defined(__cplusplus) } #endif diff --git a/src/include/module.h b/src/include/module.h index 451e24e2..d27ea522 100644 --- a/src/include/module.h +++ b/src/include/module.h @@ -16,14 +16,6 @@ extern "C" { #endif -typedef struct { - // the parameter - var_t *var_p; - - // whether the parameter can be used by reference - uint8_t byref; -} slib_par_t; - /** * @ingroup modstd * diff --git a/src/include/var.h b/src/include/var.h index 003e7949..16e0f4d2 100644 --- a/src/include/var.h +++ b/src/include/var.h @@ -47,6 +47,19 @@ extern "C" { #endif struct var_s; + +typedef struct { + // the parameter + struct var_s *var_p; + + // whether the parameter can be used by reference + uint8_t byref; +} slib_par_t; + +// signature for module callback/virtual functions +typedef int (*callback) (struct var_s *self, int param_count, slib_par_t *params, struct var_s *retval); + +// signature for internal v_funcs typedef void (*method) (struct var_s *self, struct var_s *retval); typedef struct var_s { @@ -83,6 +96,7 @@ typedef struct var_s { // object method struct { method cb; + callback mcb; uint32_t id; } fn; @@ -489,6 +503,13 @@ int v_strlen(const var_t *v); */ void v_create_func(var_p_t map, const char *name, method cb); +/** + * @ingroup var + * + * creates a callback method + */ +void v_create_callback(var_p_t map, const char *name, callback cb); + #if defined(__cplusplus) } #endif