// This file is part of SmallBASIC // // 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) 2020 Chris Warren-Smith #include #include #include #include #include #include "config.h" #include "param.h" #include "hashmap.h" #include "var.h" #define MAX_TEXT_BUFFER_LENGTH 1024 void error(var_p_t var, const char *field, int nMin, int nMax) { char message[256]; snprintf(message, sizeof(message), "Invalid Input: [%s] - expected [%d - %d] arguments", field, nMin, nMax); v_setstr(var, message); } void error(var_p_t var, const char *field, int n) { char message[256]; snprintf(message, sizeof(message), "Invalid Input: [%s] - expected [%d] arguments", field, n); v_setstr(var, message); } void error(var_p_t var, const char *text) { char message[256]; snprintf(message, sizeof(message), "Error: [%s]", text); v_setstr(var, message); } var_num_t get_num(var_p_t var) { var_num_t result; switch (var->type) { case V_INT: result = var->v.i; break; case V_NUM: result = var->v.n; break; default: result = 0.0; break; } return result; } bool get_bool(var_p_t var) { bool result; switch (var->type) { case V_INT: result = (var->v.i != 0); break; case V_NUM: result = (var->v.n != 0); break; case V_STR: result = (strncasecmp(var->v.p.ptr, "true", 4)); break; default: result = false; break; } return result; } int get_int(var_t *v) { int result; switch (v ? v->type : -1) { case V_INT: result = v->v.i; break; case V_NUM: result = v->v.n; break; default: result = 0; break; } return result; } void v_init(var_t *v) { v->type = V_INT; v->const_flag = 0; v->v.i = 0; } uint32_t v_get_capacity(uint32_t size) { return size + (size / 2) + 1; } void v_alloc_capacity(var_t *var, uint32_t size) { uint32_t capacity = v_get_capacity(size); v_capacity(var) = capacity; v_asize(var) = size; v_data(var) = (var_t *)malloc(sizeof(var_t) * capacity); if (v_data(var)) { for (uint32_t i = 0; i < capacity; i++) { var_t *e = v_elem(var, i); e->pooled = 0; v_init(e); } } } var_t *v_new() { var_t *result = (var_t *)malloc(sizeof(var_t)); result->pooled = 0; v_init(result); return result; } void v_array_free(var_t *var) { uint32_t v_size = v_capacity(var); if (v_size && v_data(var)) { for (uint32_t i = 0; i < v_size; i++) { v_free(v_elem(var, i)); } free(var->v.a.data); } } void v_free(var_t *var) { switch (var->type) { case V_STR: if (var->v.p.owner) { free(var->v.p.ptr); } break; case V_ARRAY: v_array_free(var); break; case V_MAP: assert("cannot free map"); break; } } char *v_getstr(var_t *v) { return (char *)(v->type != V_STR ? "" : v->v.p.ptr); } int set_param_int(int argc, slib_par_t *params, int param, int value, var_t *retval) { int result; if (argc < param || !params[param].byref || params[param].var_p->type != V_INT) { v_setstr(retval, "Cannot assign argument reference"); result = 0; } else { v_setint(params[param].var_p, value); result = 1; } return result; } void v_setint(var_t *var, var_int_t i) { v_free(var); var->type = V_INT; var->v.i = i; } void v_setreal(var_t *var, var_num_t n) { v_free(var); var->type = V_NUM; var->v.n = n; } void v_setstr(var_t *var, const char *str) { int length = strlen(str == nullptr ? 0 : str); v_setstrn(var, str, length); } void v_setstrn(var_t *var, const char *str, int length) { assert(var->type != V_ARRAY && var->type != V_MAP); bool isSet = false; if (var->type == V_STR) { if (strcmp(str, var->v.p.ptr) == 0) { // already set isSet = true; } else if (var->v.p.owner) { free(var->v.p.ptr); } } if (!isSet) { var->type = V_STR; var->v.p.ptr = (char *)calloc(length + 1, 1); var->v.p.length = length + 1; var->v.p.owner = 1; strncpy(var->v.p.ptr, str, length); } } int v_strlen(const var_t *v) { int result; if (v->type == V_STR) { result = v->v.p.length; if (result && v->v.p.ptr[result - 1] == '\0') { result--; } } else { result = 0; } return result; } void v_new_array(var_t *var, uint32_t size) { assert(var->type == V_INT); var->type = V_ARRAY; v_alloc_capacity(var, size); } void v_toarray1(var_t *v, uint32_t r) { v_new_array(v, r); v_maxdim(v) = 1; v_lbound(v, 0) = 0; v_ubound(v, 0) = r - 1; } void v_tomatrix(var_t *v, int r, int c) { v_new_array(v, r * c); v_maxdim(v) = 2; v_lbound(v, 0) = v_lbound(v, 1) = 0; v_ubound(v, 0) = r - 1; v_ubound(v, 1) = c - 1; } var_p_t map_add_var(var_p_t base, const char *name, int value) { var_p_t key = v_new(); v_setstr(key, name); var_p_t var = hashmap_putv(base, key); v_setint(var, value); return var; } var_p_t map_get(var_p_t base, const char *name) { var_p_t result; if (base != nullptr && base->type == V_MAP) { result = hashmap_get(base, name); } else { result = nullptr; } return result; } void map_init(var_p_t map) { assert(map->type == V_INT); v_init(map); hashmap_create(map, 0); } void map_init_id(var_p_t map, int id, int cls_id) { map_init(map); map->v.m.id = id; map->v.m.cls_id = cls_id; } int map_get_bool(var_p_t base, const char *name) { var_p_t var = map_get(base, name); return var != nullptr ? get_bool(var) : 0; } void map_set_int(var_p_t base, const char *name, var_int_t n) { var_p_t var = map_get(base, name); if (var != nullptr) { v_setint(var, n); } } int map_get_int(var_p_t base, const char *name, int def) { var_p_t var = map_get(base, name); return var != nullptr ? get_int(var) : def; } bool is_array(var_p_t var, uint32_t size) { return var != nullptr && v_is_type(var, V_ARRAY) && v_asize(var) == size; } bool is_map(var_p_t var) { return var != nullptr && v_is_type(var, V_MAP); } bool is_param_int_byref(int argc, slib_par_t *params, int arg) { return (arg < argc && params[arg].byref && (v_is_type(params[arg].var_p, V_INT) || v_is_type(params[arg].var_p, V_NUM))); } bool is_param_array(int argc, slib_par_t *params, int n) { bool result; if (n >= 0 && n < argc) { result = (params[n].var_p->type == V_ARRAY); } else { result = false; } return result; } bool is_param_num(int argc, slib_par_t *params, int n) { int result; if (n >= 0 && n < argc) { result = (params[n].var_p->type == V_NUM || params[n].var_p->type == V_INT); } else { result = false; } return result; } bool is_param_str(int argc, slib_par_t *params, int n) { int result; if (n >= 0 && n < argc) { result = (params[n].var_p->type == V_STR); } else { result = false; } return result; } bool is_param_map(int argc, slib_par_t *params, int n) { int result; if (n >= 0 && n < argc) { result = (params[n].var_p->type == V_MAP); } else { result = false; } return result; } bool is_param_nil(int argc, slib_par_t *params, int n) { int result; if (n >= 0 && n < argc) { result = (params[n].var_p->type == V_NIL); } else { result = false; } return result; } int get_id(slib_par_t *params, int n) { return params[n].var_p->v.m.id; } int get_param_int(int argc, slib_par_t *params, int n, int def) { int result; if (n >= 0 && n < argc) { switch (params[n].var_p->type) { case V_INT: result = params[n].var_p->v.i; break; case V_NUM: result = params[n].var_p->v.n; break; default: result = def; } } else { result = def; } return result; } var_int_t get_param_int_t(int argc, slib_par_t *params, int n, int def) { int result; if (n >= 0 && n < argc) { switch (params[n].var_p->type) { case V_INT: result = params[n].var_p->v.i; break; case V_NUM: result = params[n].var_p->v.n; break; default: result = def; } } else { result = def; } return result; } var_num_t get_param_num(int argc, slib_par_t *params, int n, var_num_t def) { var_num_t result; if (n >= 0 && n < argc) { switch (params[n].var_p->type) { case V_INT: result = params[n].var_p->v.i; break; case V_NUM: result = params[n].var_p->v.n; break; default: result = def; break; } } else { result = def; } return result; } var_num_t get_param_num_field(int argc, slib_par_t *params, int n, const char *field) { var_num_t result; if (is_param_map(argc, params, n)) { var_p_t v_value = map_get(params[n].var_p, field); if (v_is_type(v_value, V_INT)) { result = v_value->v.i; } else if (v_is_type(v_value, V_NUM)) { result = v_value->v.n; } else { result = 0; } } else { result = 0; } return result; } const char *get_param_str(int argc, slib_par_t *params, int n, const char *def) { const char *result; static char buf[256]; if (n >= 0 && n < argc) { switch (params[n].var_p->type) { case V_STR: result = params[n].var_p->v.p.ptr; break; case V_INT: sprintf(buf, "%lld", params[n].var_p->v.i); result = buf; break; case V_NUM: sprintf(buf, "%f", params[n].var_p->v.n); result = buf; break; default: result = ""; break; } } else { result = def == nullptr ? "" : def; } return result; } const char *get_param_str_field(int argc, slib_par_t *params, int n, const char *field) { const char *result; if (is_param_map(argc, params, n)) { var_p_t v_value = map_get(params[n].var_p, field); if (v_is_type(v_value, V_STR)) { result = v_value->v.p.ptr; } else { result = nullptr; } } else { result = nullptr; } return result; } var_num_t get_map_num(var_p_t map, const char *name) { var_p_t var = map_get(map, name); return var != nullptr ? get_num(var) : 0; } var_num_t get_array_elem_num(var_p_t array, int index) { float result; int size = v_asize(array); if (index >= 0 && index < size) { result = get_num(v_elem(array, index)); } else { result = 0.0; } return result; } int is_format_char(char c) { static char specifiers[] = {'d', 'i', 'u', 'o', 'x', 'X', 'f', 'F', 'e', 'E', 'g', 'G', 'a', 'A', 'c', 's', 'n'}; static int len = sizeof(specifiers) / sizeof(char); int result = 0; for (int i = 0; i < len && !result; i++) { result = (c == specifiers[i]); } return result; } const char *format_text(int argc, slib_par_t *params, int param) { // supported for inline use only static char buffer[MAX_TEXT_BUFFER_LENGTH]; const char *format = get_param_str(argc, params, param++, ""); const char *start = format; const int padding = 10; char *end = (char *)format; int length = 0; bool error = false; memset(buffer, '\0', MAX_TEXT_BUFFER_LENGTH); while (*end != '\0' && !error) { if (*end != '%' || param == argc) { end++; } else { while (*end != '\0') { // skip next format symbol end++; if (is_format_char(*end)) { // skip terminating format symbol char formatChar = *end; end++; int segLength = end - start; if (segLength + length + padding < MAX_TEXT_BUFFER_LENGTH) { char cNull = *end; int maxChars = MAX_TEXT_BUFFER_LENGTH - length; int count; *end = '\0'; // append to buffer, process the next single var-arg switch (params[param].var_p->type) { case V_INT: if (formatChar == 'f') { double value = (double)params[param].var_p->v.i; count = snprintf(buffer + length, maxChars, start, value); } else if (formatChar != 's') { count = snprintf(buffer + length, maxChars, start, params[param].var_p->v.i); } else { count = -1; } break; case V_NUM: if (formatChar != 's') { count = snprintf(buffer + length, maxChars, start, params[param].var_p->v.n); } else { count = -1; } break; case V_STR: if (formatChar == 's') { count = snprintf(buffer + length, maxChars, start, params[param].var_p->v.p.ptr); } else { count = -1; } break; default: count = -1; break; } param++; length += count; *end = cNull; start = end; if (count < 0 || count >= maxChars) { error = true; break; } } break; } else if (*end != '.' && !isdigit(*end)) { // non-formatting symbol buffer[length++] = *end; buffer[length] = '\0'; end++; start = end; break; } } } } if (error) { memset(buffer, '\0', MAX_TEXT_BUFFER_LENGTH); sprintf(buffer, "ERROR: Invalid text format: %s", format); } else { int segLength = end - start; if (segLength && (segLength + length + padding < MAX_TEXT_BUFFER_LENGTH)) { strncpy(buffer + length, start, segLength); buffer[length + segLength + 1] = '\0'; } } return buffer; } 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 = nullptr; 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 = nullptr; v_func->v.fn.mcb = cb; v_func->v.fn.id = 0; }