/*********************************************************************************************\ * Add const virtual members to classes and modules from C static tables * * This allows to creates hundreds of constant members (integers, strings...) * stored in a C array instead of explicit berry members. * * It has the following advantages: * - consumes much less flash memory, especially with hundreds of members * - allows C preprocessor to compute the value at compile time (instead of pure numerical numbers) * - allows to compute pointer addresses to C structures * * Takes a pointer to be_const_member_t array and size * Returns true if a match was found. In such case the result is on Berry stack * * Encoding depend on prefix (which is skipped when matching names): * - `COLOR_WHITE` int value * - `$SYMBOL_OK"` string pointer * - `&seg7_font` comptr * - `*my_func` native function - the function is called and return value passed back. * This allows to create dynamic virtual members that are the result of a call. \*********************************************************************************************/ #include "be_mapping.h" #include "be_exec.h" #include "be_string.h" #include "be_module.h" #include /*********************************************************************************************\ * Takes a pointer to be_const_member_t array and size * Returns true if a match was found. In such case the result is on Berry stack * * Can be called directly by `member()` function, takes the name of the member from * berry stack at position 1. * * Encoding depend on prefix (which is skipped when matching names): * - `COLOR_WHITE` int value * - `$SYMBOL_OK"` string pointer * - `&seg7_font` comptr * - `@func` Berry native function * - `*my_func` native function - the function is called and return value passed back. * This allows to create dynamic virtual members that are the result of a call. * - `/my_class` a Berry class * * The array must be lexically sorted, but the sort function must ignore the prefix `$`, `&`, `*` \*********************************************************************************************/ static bbool be_const_member_dual(bvm *vm, const be_const_member_t * definitions, size_t def_len, bbool is_method) { int32_t arg_idx = is_method ? 2 : 1; // index for name argument, 1 if module, 2 if method since 1 is self. int32_t argc = be_top(vm); // Get the number of arguments if (argc == arg_idx && be_isstring(vm, arg_idx)) { const char * needle = be_tostring(vm, arg_idx); int32_t idx; idx = be_map_bin_search(needle, &definitions[0].name, sizeof(definitions[0]), def_len); if (idx >= 0) { // we did have a match const char * key = definitions[idx].name; switch (key[0]) { // switch depending on the first char of the key, indicating the type case '$': // string be_pushstring(vm, (const char*) definitions[idx].value); break; case '@': // native function be_pushntvfunction(vm, (bntvfunc) definitions[idx].value); break; case '&': // pointer be_pushcomptr(vm, (void*) definitions[idx].value); break; case '/': be_pushntvclass(vm, (const struct bclass*) definitions[idx].value); break; case '*': // call to a native function { bntvfunc f = (bntvfunc) definitions[idx].value; int ret = f(vm); if ((ret == BE_OK) && !be_isnil(vm, -1)) { return btrue; } else { return bfalse; } break; } case '>': // call to a ctype function { be_ctype_var_args_t* args = (be_ctype_var_args_t*) definitions[idx].value; be_pop(vm, be_top(vm) - 1); // make sure we have only the instance left on the stack int ret = be_call_c_func(vm, args->func, args->return_type, NULL); if ((ret == BE_OK) && !be_isnil(vm, -1)) { return btrue; } else { return bfalse; } break; } default: // int be_pushint(vm, definitions[idx].value); break; } return btrue; } } return bfalse; } bbool be_const_module_member(bvm *vm, const be_const_member_t * definitions, size_t def_len) { return be_const_member_dual(vm, definitions, def_len, bfalse); // call for module, non-method } /* This version raises an exception if the attribute is not found */ void be_const_module_member_raise(bvm *vm, const be_const_member_t * definitions, size_t def_len) { if (!be_const_member_dual(vm, definitions, def_len, bfalse)) { be_module_load(vm, be_newstr(vm, "undefined")); } } bbool be_const_class_member(bvm *vm, const be_const_member_t * definitions, size_t def_len) { return be_const_member_dual(vm, definitions, def_len, btrue); // call for method } /* This version raises an exception if the attribute is not found */ void be_const_class_member_raise(bvm *vm, const be_const_member_t * definitions, size_t def_len) { if (!be_const_member_dual(vm, definitions, def_len, btrue)) { be_module_load(vm, be_newstr(vm, "undefined")); } }