diff --git a/CMakeLists.txt b/CMakeLists.txt index 46f3ca5..4223f6e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -96,6 +96,19 @@ add_executable(fzc # Virtual machine add_executable(fz + src/vm/handlers/group_and_or.c + src/vm/handlers/group_cmp.c + src/vm/handlers/group_ctx.c + src/vm/handlers/group_flow.c + src/vm/handlers/group_fn.c + src/vm/handlers/group_math.c + src/vm/handlers/group_not.c + src/vm/handlers/group_obj.c + src/vm/handlers/group_objcmp.c + src/vm/handlers/group_stack.c + src/vm/handlers/group_str.c + src/vm/handlers/group_strcmp.c + src/vm/handlers/master.c src/vm/fzl_vm.c src/vm/fzl_vm_env.c src/vm/fzl_vm_debug.c diff --git a/include/vm/fzl_vm.h b/include/vm/fzl_vm.h index 33e2f58..45f93f2 100644 --- a/include/vm/fzl_vm.h +++ b/include/vm/fzl_vm.h @@ -3,6 +3,7 @@ #include #include // NOTE: move outside compiler scope to common +#include #include #include #include @@ -23,6 +24,9 @@ typedef struct size_t ip; + bool allow_infinite; + size_t max_iter; + // Data stack fzl_value_t dstack[256]; size_t dtop; @@ -40,7 +44,7 @@ typedef struct fzl_vm_t *fzl_mkvm(const char *filepath); // Load and run code -int fzl_vm_run(fzl_vm_t *vm, bool allow_infinite, uint64_t max_iter); +int fzl_vm_run(fzl_vm_t *vm); // Get last VM error message char *fzl_vm_geterror(); diff --git a/include/vm/internals/opcode.h b/include/vm/internals/opcode.h new file mode 100644 index 0000000..00d9af7 --- /dev/null +++ b/include/vm/internals/opcode.h @@ -0,0 +1,108 @@ +#ifndef INTERNALS_OPCODE_H +#define INTERNALS_OPCODE_H + +#include "vm/fzl_vm.h" +#include + +// Opcode handler descriptor +typedef int (*opcode_handler)(fzl_vm_t *); + +extern char last_err[256]; +extern size_t last_jmp_ip; +extern size_t total_jmp_on_same; + +void print_object(fzl_vm_object_t *obj); +void print_err_data(fzl_vm_t *vm); + +// --- stack --- + +extern const opcode_handler group_stack[]; + +int op_loadconst(fzl_vm_t *vm); +int op_call_builtin(fzl_vm_t *vm); +int op_pushnum(fzl_vm_t *vm); +int op_pushbool(fzl_vm_t *vm); + +// --- context --- + +extern const opcode_handler group_ctx[]; + +int op_mk_envtable(fzl_vm_t *vm); +int op_rm_envtable(fzl_vm_t *vm); +int op_store(fzl_vm_t *vm); +int op_lookup_env(fzl_vm_t *vm); +int op_write(fzl_vm_t *vm); + +// --- flow --- + +extern const opcode_handler group_flow[]; + +int op_jmp(fzl_vm_t *vm); +int op_jz(fzl_vm_t *vm); + +// --- functions --- + +extern const opcode_handler group_fn[]; + +int op_call(fzl_vm_t *vm); +int op_ret(fzl_vm_t *vm); +int op_call_extern(fzl_vm_t *vm); + +// --- objects --- + +extern const opcode_handler group_obj[]; + +int op_alloc_object(fzl_vm_t *vm); +int op_get_field(fzl_vm_t *vm); +int op_set_field(fzl_vm_t *vm); +int op_escape_object(fzl_vm_t *vm); +int op_dereference(fzl_vm_t *vm); + +// --- math --- + +extern const opcode_handler group_math[]; + +int op_add(fzl_vm_t *vm); +int op_sub(fzl_vm_t *vm); +int op_mul(fzl_vm_t *vm); +int op_div(fzl_vm_t *vm); +int op_pow(fzl_vm_t *vm); +int op_mod(fzl_vm_t *vm); +int op_neg(fzl_vm_t *vm); + +// --- strings --- + +extern const opcode_handler group_str[]; + +int op_build_str(fzl_vm_t *vm); + +// --- conditions --- + +extern const opcode_handler group_cmp[]; +int op_cmp_eq(fzl_vm_t *vm); +int op_cmp_neq(fzl_vm_t *vm); +int op_cmp_lt(fzl_vm_t *vm); +int op_cmp_lteq(fzl_vm_t *vm); +int op_cmp_gt(fzl_vm_t *vm); +int op_cmp_gteq(fzl_vm_t *vm); + +extern const opcode_handler group_strcmp[]; +int op_cmp_eq_str(fzl_vm_t *vm); +int op_cmp_neq_str(fzl_vm_t *vm); + +extern const opcode_handler group_objcmp[]; +int op_cmp_eq_obj(fzl_vm_t *vm); +int op_cmp_neq_obj(fzl_vm_t *vm); + +extern const opcode_handler group_and_or[]; +int op_and(fzl_vm_t *vm); +int op_or(fzl_vm_t *vm); + +extern const opcode_handler group_not[]; +int op_not(fzl_vm_t *vm); + +// ------------------------ + +extern const opcode_handler *const master_tbl[]; + +#endif // INTERNALS_OPCODE_H diff --git a/src/vm/fzl_vm.c b/src/vm/fzl_vm.c index a10f088..3842fbb 100644 --- a/src/vm/fzl_vm.c +++ b/src/vm/fzl_vm.c @@ -12,8 +12,8 @@ #include #include #include +#include -char build_result[512]; char last_err[256]; size_t last_jmp_ip = 0; size_t total_jmp_on_same = 0; @@ -29,20 +29,7 @@ fzl_vm_t *fzl_mkvm(const char *filepath) return NULL; } - vm->consts = NULL; - vm->consts_count = 0; - vm->ops = NULL; - vm->ops_count = 0; - vm->externs = NULL; - vm->externs_count = 0; - vm->ip = 0; - vm->dtop = 0; - vm->ctop = 0; - vm->etop = 0; - - memset(vm->dstack, 0, sizeof(vm->dstack)); - memset(vm->cstack, 0, sizeof(vm->cstack)); - memset(vm->estack, 0, sizeof(vm->estack)); + memset(vm, 0, sizeof(fzl_vm_t)); FILE *fd = fopen(filepath, "rb"); if (!fd) @@ -127,57 +114,6 @@ fzl_vm_t *fzl_mkvm(const char *filepath) return vm; } -// Escape objects with nested objects check -static void escape_recursive(fzl_vm_env_t *current, fzl_vm_env_t *parent, - fzl_vm_object_t *obj) -{ - if (!current || !parent || !obj) - return; - - bool found_in_current = false; - for (size_t i = 0; i < current->owned_objects_count; i++) - { - if (current->owned_objects[i] == obj) - { - current->owned_objects[i] = NULL; - found_in_current = true; - break; - } - } - - if (found_in_current) - { - bool found_in_parent = false; - for (size_t i = 0; i < parent->owned_objects_count; i++) - { - if (parent->owned_objects[i] == obj) - { - found_in_parent = true; - break; - } - } - - if (!found_in_parent) - { - if (parent->owned_objects_remainder <= parent->owned_objects_count) - { - parent->owned_objects_remainder *= 2; - parent->owned_objects = - realloc(parent->owned_objects, parent->owned_objects_remainder * - sizeof(fzl_vm_object_t *)); - } - - parent->owned_objects[parent->owned_objects_count++] = obj; - } - } - - for (size_t i = 0; i < obj->values_count; i++) - { - if (obj->values[i].type == FZL_TYPE_OBJECT && obj->values[i].as.obj != NULL) - escape_recursive(current, parent, obj->values[i].as.obj); - } -} - void print_object(fzl_vm_object_t *obj) { if (!obj) @@ -283,7 +219,7 @@ void print_err_data(fzl_vm_t *vm) printf("\t> [%zd] %lu\n", i, vm->cstack[i]); } -int fzl_vm_run(fzl_vm_t *vm, bool allow_infinite, uint64_t max_iter) +int fzl_vm_run(fzl_vm_t *vm) { if (!vm) { @@ -291,882 +227,28 @@ int fzl_vm_run(fzl_vm_t *vm, bool allow_infinite, uint64_t max_iter) return -1; } - vm->ip = 0; + vm->ip = 0; + int result = 0; while (vm->ip < vm->ops_count) { fzl_opcode_t opcode = vm->ops[vm->ip]; + size_t op_group = (opcode >> 16); + size_t op_fnnum = (opcode & 0xFF); - switch (opcode) + if (master_tbl[op_group] == NULL || master_tbl[op_group][op_fnnum] == NULL) { - case FZL_OPCODE_BUILD_STR: - { - uint64_t n = vm->ops[++vm->ip]; - if (vm->dtop < n) - { - snprintf(last_err, sizeof(last_err), - "runtime error - expected %zu value(s) in dstack", n); - print_err_data(vm); - return -1; - } - - char temp[64]; - size_t gi = 0; - memset(build_result, 0, sizeof(build_result)); - for (size_t i = 0; i < n; i++) - { - if (gi >= 512) - { - for (size_t i = 511; i > 508; i--) - build_result[i] = '.'; - break; - } - - memset(temp, 0, sizeof(temp)); - fzl_value_t val = vm->dstack[i]; - - switch (val.type) - { - case FZL_TYPE_UINT: - snprintf(temp, sizeof(temp), "%zu", val.as.u); - strcpy(build_result + gi, temp); - gi += strlen(temp); - break; - - case FZL_TYPE_UINT64: - snprintf(temp, sizeof(temp), "%lu", val.as.u64); - strcpy(build_result + gi, temp); - gi += strlen(temp); - break; - - case FZL_TYPE_UINT32: - snprintf(temp, sizeof(temp), "%u", val.as.u32); - strcpy(build_result + gi, temp); - gi += strlen(temp); - break; - - case FZL_TYPE_UINT16: - snprintf(temp, sizeof(temp), "%u", val.as.u16); - strcpy(build_result + gi, temp); - gi += strlen(temp); - break; - - case FZL_TYPE_UINT8: - snprintf(temp, sizeof(temp), "%u", val.as.u8); - strcpy(build_result + gi, temp); - gi += strlen(temp); - break; - - case FZL_TYPE_INT: - snprintf(temp, sizeof(temp), "%zd", val.as.i); - strcpy(build_result + gi, temp); - gi += strlen(temp); - break; - - case FZL_TYPE_INT64: - snprintf(temp, sizeof(temp), "%ld", val.as.i64); - strcpy(build_result + gi, temp); - gi += strlen(temp); - break; - - case FZL_TYPE_INT32: - snprintf(temp, sizeof(temp), "%d", val.as.i32); - strcpy(build_result + gi, temp); - gi += strlen(temp); - break; - - case FZL_TYPE_INT16: - snprintf(temp, sizeof(temp), "%d", val.as.i16); - strcpy(build_result + gi, temp); - gi += strlen(temp); - break; - - case FZL_TYPE_INT8: - snprintf(temp, sizeof(temp), "%d", val.as.i8); - strcpy(build_result + gi, temp); - gi += strlen(temp); - break; - - case FZL_TYPE_BOOL: - snprintf(build_result + gi, sizeof(build_result) - gi, "%s", - val.as.b ? "true" : "false"); - gi += val.as.b ? 4 : 5; - break; - - case FZL_TYPE_CHAR: - snprintf(build_result + gi, sizeof(build_result) - gi, "%d", - val.as.c); - gi += 1; - break; - - case FZL_TYPE_STRING: - snprintf(build_result + gi, sizeof(build_result) - gi, "%s", - val.as.s.value); - gi += val.as.s.length; - break; - - case FZL_TYPE_EXTERN_FN: - snprintf(temp, sizeof(temp), "", val.as.u); - strcpy(build_result + gi, temp); - gi += strlen(temp); - break; - - case FZL_TYPE_OBJECT: - snprintf(temp, sizeof(temp), "", val.as.obj); - strcpy(build_result + gi, temp); - gi += strlen(temp); - break; - - case FZL_TYPE_NULL: - snprintf(build_result + gi, sizeof(build_result) - gi, ""); - gi += 6; - break; - } - } - - vm->dtop -= n; - vm->dstack[vm->dtop++] = - (fzl_value_t){.type = FZL_TYPE_STRING, - .as.s.length = strlen(build_result), - .as.s.value = build_result}; - break; - } - - case FZL_OPCODE_DEREFERENCE: - { - if (vm->dtop < 1) - { - snprintf(last_err, sizeof(last_err), - "runtime error - expected value in data stack"); - print_err_data(vm); - return -1; - } - - fzl_value_t addr = vm->dstack[--vm->dtop]; - if (addr.type < FZL_TYPE_INT8) - { - snprintf(last_err, sizeof(last_err), - "runtime error - expected address"); - print_err_data(vm); - return -1; - } - - if (addr.as.u == 0) - { - vm->dstack[vm->dtop++] = - (fzl_value_t){.type = FZL_TYPE_OBJECT, .as.obj = NULL}; - break; - } - - vm->dstack[vm->dtop++] = (fzl_value_t){ - .type = FZL_TYPE_OBJECT, .as.obj = (fzl_vm_object_t *)addr.as.u}; - break; - } - - case FZL_OPCODE_CMP_EQ_STR: - { - if (vm->dtop < 2) - { - snprintf(last_err, sizeof(last_err), - "runtime error - expected two values in data stack"); - print_err_data(vm); - return -1; - } - fzl_value_t left = vm->dstack[--vm->dtop]; - fzl_value_t right = vm->dstack[--vm->dtop]; - - int result = strcmp(left.as.s.value, right.as.s.value); - vm->dstack[vm->dtop++] = - (fzl_value_t){.type = FZL_TYPE_BOOL, .as.b = result == 0}; - break; - } - - case FZL_OPCODE_CMP_NEQ_STR: - { - if (vm->dtop < 2) - { - snprintf(last_err, sizeof(last_err), - "runtime error - expected two values in data stack"); - print_err_data(vm); - return -1; - } - fzl_value_t left = vm->dstack[--vm->dtop]; - fzl_value_t right = vm->dstack[--vm->dtop]; - - int result = strcmp(left.as.s.value, right.as.s.value); - vm->dstack[vm->dtop++] = - (fzl_value_t){.type = FZL_TYPE_BOOL, .as.b = result != 0}; - break; - } - - case FZL_OPCODE_CMP_EQ_OBJ: - { - if (vm->dtop < 2) - { - snprintf(last_err, sizeof(last_err), - "runtime error - expected two values in data stack"); - print_err_data(vm); - return -1; - } - fzl_value_t left = vm->dstack[--vm->dtop]; - fzl_value_t right = vm->dstack[--vm->dtop]; - - vm->dstack[vm->dtop++] = (fzl_value_t){ - .type = FZL_TYPE_BOOL, .as.b = left.as.obj == right.as.obj}; - break; - } - - case FZL_OPCODE_CMP_NEQ_OBJ: - { - if (vm->dtop < 2) - { - snprintf(last_err, sizeof(last_err), - "runtime error - expected two values in data stack"); - print_err_data(vm); - return -1; - } - fzl_value_t left = vm->dstack[--vm->dtop]; - fzl_value_t right = vm->dstack[--vm->dtop]; - - vm->dstack[vm->dtop++] = (fzl_value_t){ - .type = FZL_TYPE_BOOL, .as.b = left.as.obj != right.as.obj}; - break; - } - - case FZL_OPCODE_ESCAPE_OBJECT: - { - if (vm->dtop < 1) - { - snprintf(last_err, sizeof(last_err), - "runtime error - missing value in data stack"); - print_err_data(vm); - return -1; - } - fzl_value_t obj = vm->dstack[--vm->dtop]; - if (obj.type != FZL_TYPE_OBJECT) - { - snprintf(last_err, sizeof(last_err), - "runtime error - cannot escape non-object type"); - print_err_data(vm); - return -1; - } - - fzl_vm_env_t *parent = vm->estack[vm->etop - 2]; - fzl_vm_env_t *current = vm->estack[vm->etop - 1]; - if (vm->etop < 2) - { - snprintf(last_err, sizeof(last_err), - "runtime error - cannot escape object from global env"); - print_err_data(vm); - return -1; - } - - escape_recursive(current, parent, obj.as.obj); - vm->dstack[vm->dtop++] = obj; - - break; - } - - case FZL_OPCODE_ALLOC_OBJECT: - { - uint64_t values_count = vm->ops[++vm->ip]; - fzl_vm_object_t *obj = malloc(sizeof(fzl_vm_object_t)); - - if (values_count > 0) - obj->values = malloc(values_count * sizeof(fzl_value_t)); - obj->values_count = values_count; - memset(obj->values, 0, values_count * sizeof(fzl_value_t)); - - fzl_vm_env_t *current_env = vm->estack[vm->etop - 1]; - if (current_env->owned_objects_remainder <= - current_env->owned_objects_count) - { - current_env->owned_objects_remainder *= 2; - current_env->owned_objects = realloc( - current_env->owned_objects, - current_env->owned_objects_remainder * sizeof(fzl_vm_object_t *)); - } - - current_env->owned_objects[current_env->owned_objects_count++] = obj; - vm->dstack[vm->dtop++] = - (fzl_value_t){.type = FZL_TYPE_OBJECT, .as.obj = obj}; - break; - } - - case FZL_OPCODE_GET_FIELD: - { - if (vm->dtop < 1) - { - snprintf(last_err, sizeof(last_err), - "runtime error - expected value in data stack"); - print_err_data(vm); - return -1; - } - - uint64_t field_slot = vm->ops[++vm->ip]; - fzl_value_t val = vm->dstack[--vm->dtop]; - if (val.type != FZL_TYPE_OBJECT) - { - snprintf( - last_err, sizeof(last_err), - "runtime error - cannot access field #%lu of non-object type", - field_slot); - print_err_data(vm); - return -1; - } - - fzl_vm_object_t *obj = val.as.obj; - if (obj->values_count <= field_slot) - { - snprintf(last_err, sizeof(last_err), - "runtime error - cannot access field #%lu", field_slot); - print_err_data(vm); - return -1; - } - - vm->dstack[vm->dtop++] = obj->values[field_slot]; - break; - } - - case FZL_OPCODE_SET_FIELD: - { - if (vm->dtop < 2) - { - snprintf(last_err, sizeof(last_err), - "runtime error - expected two values in data stack"); - print_err_data(vm); - return -1; - } - - uint64_t field_slot = vm->ops[++vm->ip]; - fzl_value_t objval = vm->dstack[--vm->dtop]; - fzl_value_t wrval = vm->dstack[--vm->dtop]; - if (objval.type != FZL_TYPE_OBJECT) - { - snprintf( - last_err, sizeof(last_err), - "runtime error - cannot access field #%lu of non-object type", - field_slot); - print_err_data(vm); - return -1; - } - - fzl_vm_object_t *obj = objval.as.obj; - if (obj->values_count <= field_slot) - { - snprintf(last_err, sizeof(last_err), - "runtime error - cannot access field #%lu", field_slot); - print_err_data(vm); - return -1; - } - - obj->values[field_slot] = wrval; - break; - } - - case FZL_OPCODE_CALL: - { - uint64_t ip = vm->ops[++vm->ip]; - vm->cstack[vm->ctop++] = vm->ip; - vm->ip = ip - 1; - break; - } - - case FZL_OPCODE_RET: - { - if (vm->ctop < 1) - { - snprintf(last_err, sizeof(last_err), - "runtime error - no place to jump"); - print_err_data(vm); - return -1; - } - - vm->ip = vm->cstack[--vm->ctop]; - break; - } - - case FZL_OPCODE_CMP_EQ: - if (vm->dtop < 2) - { - snprintf(last_err, sizeof(last_err), - "runtime error - expected two values in data stack"); - print_err_data(vm); - return -1; - } - - fzl_value_t req = vm->dstack[--vm->dtop]; - fzl_value_t leq = vm->dstack[--vm->dtop]; - vm->dstack[vm->dtop++] = - (fzl_value_t){.type = FZL_TYPE_BOOL, .as.b = leq.as.u == req.as.u}; - break; - - case FZL_OPCODE_CMP_NEQ: - if (vm->dtop < 2) - { - snprintf(last_err, sizeof(last_err), - "runtime error - expected two values in data stack"); - print_err_data(vm); - return -1; - } - - fzl_value_t rneq = vm->dstack[--vm->dtop]; - fzl_value_t lneq = vm->dstack[--vm->dtop]; - vm->dstack[vm->dtop++] = (fzl_value_t){.type = FZL_TYPE_BOOL, - .as.b = lneq.as.u != rneq.as.u}; - break; - - case FZL_OPCODE_CMP_LT: - if (vm->dtop < 2) - { - snprintf(last_err, sizeof(last_err), - "runtime error - expected two values in data stack"); - print_err_data(vm); - return -1; - } - - fzl_value_t rlt = vm->dstack[--vm->dtop]; - fzl_value_t llt = vm->dstack[--vm->dtop]; - vm->dstack[vm->dtop++] = - (fzl_value_t){.type = FZL_TYPE_BOOL, .as.b = llt.as.u < rlt.as.u}; - break; - - case FZL_OPCODE_CMP_LTEQ: - if (vm->dtop < 2) - { - snprintf(last_err, sizeof(last_err), - "runtime error - expected two values in data stack"); - print_err_data(vm); - return -1; - } - - fzl_value_t rlteq = vm->dstack[--vm->dtop]; - fzl_value_t llteq = vm->dstack[--vm->dtop]; - vm->dstack[vm->dtop++] = (fzl_value_t){ - .type = FZL_TYPE_BOOL, .as.b = llteq.as.u <= rlteq.as.u}; - break; - - case FZL_OPCODE_CMP_GT: - if (vm->dtop < 2) - { - snprintf(last_err, sizeof(last_err), - "runtime error - expected two values in data stack"); - print_err_data(vm); - return -1; - } - - fzl_value_t rgt = vm->dstack[--vm->dtop]; - fzl_value_t lgt = vm->dstack[--vm->dtop]; - vm->dstack[vm->dtop++] = - (fzl_value_t){.type = FZL_TYPE_BOOL, .as.b = lgt.as.u > rgt.as.u}; - break; - - case FZL_OPCODE_CMP_GTEQ: - if (vm->dtop < 2) - { - snprintf(last_err, sizeof(last_err), - "runtime error - expected two values in data stack"); - print_err_data(vm); - return -1; - } - - fzl_value_t rgteq = vm->dstack[--vm->dtop]; - fzl_value_t lgteq = vm->dstack[--vm->dtop]; - vm->dstack[vm->dtop++] = (fzl_value_t){ - .type = FZL_TYPE_BOOL, .as.b = lgteq.as.u >= rgteq.as.u}; - break; - - case FZL_OPCODE_NOT: - if (vm->dtop < 1) - { - snprintf(last_err, sizeof(last_err), - "runtime error - expected value in data stack"); - print_err_data(vm); - return -1; - } - - fzl_value_t notval = vm->dstack[--vm->dtop]; - vm->dstack[vm->dtop++] = - (fzl_value_t){.type = FZL_TYPE_BOOL, .as.b = !notval.as.b}; - break; - - case FZL_OPCODE_JZ: - if (vm->dtop < 1) - { - snprintf(last_err, sizeof(last_err), - "runtime error - expected value in data stack"); - print_err_data(vm); - return -1; - } - - fzl_value_t jzval = vm->dstack[--vm->dtop]; - uint64_t jzip = vm->ops[++vm->ip] - 1; - if (jzval.as.b == 0) - vm->ip = jzip; - break; - - case FZL_OPCODE_JMP: - { - if (vm->ops_count <= vm->ip) - { - snprintf(last_err, sizeof(last_err), - "runtime error - expected JMP addr"); - return -1; - } - - uint64_t jmp_ip = vm->ops[++vm->ip]; - if (!allow_infinite) - { - if (jmp_ip == last_jmp_ip) - total_jmp_on_same++; - else - total_jmp_on_same = 0; - - if (total_jmp_on_same > max_iter) - { - snprintf( - last_err, sizeof(last_err), - "runtime error - exceeded max loop jumps (infinite loop?)\n" - "If you thinks there's mistake, change jumps limit with " - "--max-iter=N"); - return -1; - } - } - - vm->ip = jmp_ip - 1; - last_jmp_ip = jmp_ip; - break; - } - - case FZL_OPCODE_AND: - if (vm->dtop < 2) - { - snprintf(last_err, sizeof(last_err), - "runtime error - expected two values in data stack"); - print_err_data(vm); - return -1; - } - - fzl_value_t land = vm->dstack[--vm->dtop]; - fzl_value_t rand = vm->dstack[--vm->dtop]; - vm->dstack[vm->dtop++] = (fzl_value_t){.type = FZL_TYPE_BOOL, - .as.b = land.as.b && rand.as.b}; - break; - - case FZL_OPCODE_OR: - if (vm->dtop < 2) - { - snprintf(last_err, sizeof(last_err), - "runtime error - expected two values in data stack"); - print_err_data(vm); - return -1; - } - - fzl_value_t lor = vm->dstack[--vm->dtop]; - fzl_value_t ror = vm->dstack[--vm->dtop]; - vm->dstack[vm->dtop++] = - (fzl_value_t){.type = FZL_TYPE_BOOL, .as.b = lor.as.b || ror.as.b}; - break; - - case FZL_OPCODE_STORE: - if (vm->dtop < 1) - { - snprintf(last_err, sizeof(last_err), - "runtime error - expected var name in data stack"); - print_err_data(vm); - return -1; - } - - fzl_value_t varname = vm->dstack[--vm->dtop]; - fzl_env_storevalue(vm->estack[vm->etop - 1], (char *)varname.as.s.value, - (fzl_value_t){}); - - break; - - case FZL_OPCODE_WRITE: - if (vm->dtop < 2) - { - snprintf(last_err, sizeof(last_err), - "runtime error - expected 2 values in data stack"); - print_err_data(vm); - return -1; - } - - fzl_value_t writevar = vm->dstack[--vm->dtop]; - fzl_value_t value = vm->dstack[--vm->dtop]; - - char *wrname = (char *)writevar.as.s.value; - bool wrfound; - fzl_value_t wrenv; - ssize_t wrlvl = -1; - for (ssize_t i = vm->etop - 1; i >= 0; i--) - { - if ((wrfound = fzl_env_lookup(vm->estack[i], wrname, &wrenv))) - { - wrlvl = i; - break; - } - } - - if (wrlvl <= -1) - { - snprintf(last_err, sizeof(last_err), - "runtime error - name '%s' not found in this scope", wrname); - print_err_data(vm); - return -1; - } - - fzl_vm_env_wresult_t wresult = - fzl_env_writevalue(vm->estack[wrlvl], (char *)wrname, value); - if (wresult != FZL_VM_ENV_WRESULT_OK) - { - switch (wresult) - { - case FZL_VM_ENV_WRESULT_ERR: - snprintf(last_err, sizeof(last_err), - "runtime error - failed to write variable"); - print_err_data(vm); - return -1; - - case FZL_VM_ENV_WRESULT_ERR_EXISTS: - snprintf(last_err, sizeof(last_err), - "runtime error - redefenition of '%s'", - writevar.as.s.value); - print_err_data(vm); - return -1; - - case FZL_VM_ENV_WRESULT_ERR_NOT_FOUND: - snprintf(last_err, sizeof(last_err), - "runtime error - cannot find name '%s'", - writevar.as.s.value); - print_err_data(vm); - return -1; - - default: - print_err_data(vm); - return -1; - } - } - - // vm->dstack[vm->dtop++] = value; - // NOTE: add POP opcode to prevent - // stack overflow! - break; - - case FZL_OPCODE_MK_ENVTABLE: - vm->estack[vm->etop++] = fzl_mkenv(); - break; - - case FZL_OPCODE_RM_ENVTABLE: - fzl_freeenv(vm->estack[--vm->etop]); - break; - - case FZL_OPCODE_LOOKUP_ENV: - { - if (vm->dtop < 1) - { - snprintf(last_err, sizeof(last_err), - "runtime error - LOOKUP_ENV: empty data stack"); - print_err_data(vm); - return -1; - } - - fzl_value_t val = vm->dstack[--vm->dtop]; - fzl_value_t env_val; - bool result; - for (ssize_t i = vm->etop - 1; i >= 0; i--) - { - if ((result = fzl_env_lookup(vm->estack[i], (char *)val.as.s.value, - &env_val))) - break; - } - vm->dstack[vm->dtop++] = env_val; - - break; - } - - case FZL_OPCODE_CALL_BUILTIN: - { - uint64_t fn_id = vm->ops[++vm->ip]; - if (fn_id == FZL_BUILTIN_PRINTLINE) - { - fzl_value_t arg = vm->dstack[--vm->dtop]; - if (arg.type == FZL_TYPE_STRING) - printf("%s\n", arg.as.s.value); - else if (arg.type == FZL_TYPE_CHAR) - printf("%c\n", arg.as.c); - else if (arg.type == FZL_TYPE_INT) - printf("%ld\n", arg.as.i); - else if (arg.type == FZL_TYPE_UINT) - printf("%lu\n", arg.as.u); - else if (arg.type == FZL_TYPE_BOOL) - printf("%s\n", arg.as.b ? "true" : "false"); - else if (arg.type == FZL_TYPE_NULL) - printf("\n"); - else if (arg.type == FZL_TYPE_OBJECT) - { - print_object(arg.as.obj); - printf("\n"); - } - } - else if (fn_id == FZL_BUILTIN_EXIT) - { - fzl_value_t arg = vm->dstack[--vm->dtop]; - return arg.as.i; - } - else - { - snprintf(last_err, sizeof(last_err), - "runtime error - undefined builtin %lu", fn_id); - print_err_data(vm); - return -1; - } - } - break; - - case FZL_OPCODE_LOADCONST: - { - uint64_t cn = vm->ops[++vm->ip]; - if (vm->consts_count <= cn) - { - snprintf(last_err, sizeof(last_err), "runtime error - unknown const"); - print_err_data(vm); - return -1; - } - fzl_const_t c = vm->consts[cn]; - vm->dstack[vm->dtop++] = - (fzl_value_t){.type = FZL_TYPE_STRING, - .as.s.length = c.as.string.length, - .as.s.value = c.as.string.chars}; - } - break; - - case FZL_OPCODE_ADD: - if (vm->dtop < 2) - { - snprintf(last_err, sizeof(last_err), - "runtime error - expected 2 values in stack"); - print_err_data(vm); - return -1; - } - fzl_value_t add1 = vm->dstack[--vm->dtop]; - fzl_value_t add2 = vm->dstack[--vm->dtop]; - vm->dstack[vm->dtop++] = - (fzl_value_t){.type = add1.type, .as.u = add1.as.u + add2.as.u}; - break; - - case FZL_OPCODE_SUB: - if (vm->dtop < 2) - { - snprintf(last_err, sizeof(last_err), - "runtime error - expected 2 values in stack"); - print_err_data(vm); - return -1; - } - fzl_value_t sub2 = vm->dstack[--vm->dtop]; - fzl_value_t sub1 = vm->dstack[--vm->dtop]; - vm->dstack[vm->dtop++] = - (fzl_value_t){.type = sub1.type, .as.u = sub1.as.u - sub2.as.u}; - break; - - case FZL_OPCODE_MUL: - if (vm->dtop < 2) - { - snprintf(last_err, sizeof(last_err), - "runtime error - expected 2 values in stack"); - print_err_data(vm); - return -1; - } - fzl_value_t mul1 = vm->dstack[--vm->dtop]; - fzl_value_t mul2 = vm->dstack[--vm->dtop]; - vm->dstack[vm->dtop++] = - (fzl_value_t){.type = mul1.type, .as.u = mul1.as.u * mul2.as.u}; - break; - - case FZL_OPCODE_DIV: - if (vm->dtop < 2) - { - snprintf(last_err, sizeof(last_err), - "runtime error - expected 2 values in stack"); - print_err_data(vm); - return -1; - } - fzl_value_t div1 = vm->dstack[--vm->dtop]; - fzl_value_t div2 = vm->dstack[--vm->dtop]; - vm->dstack[vm->dtop++] = - (fzl_value_t){.type = div1.type, .as.u = div2.as.u / div1.as.u}; - break; - - case FZL_OPCODE_POW: - if (vm->dtop < 2) - { - snprintf(last_err, sizeof(last_err), - "runtime error - expected 2 values in stack"); - print_err_data(vm); - return -1; - } - fzl_value_t pow2 = vm->dstack[--vm->dtop]; - fzl_value_t pow1 = vm->dstack[--vm->dtop]; - - uint64_t pow_n = pow1.as.u; - uint64_t pow_res = pow_n; - uint64_t pow_times = pow2.as.u; - for (size_t i = 1; i < pow_times; i++) - pow_res *= pow_n; - - vm->dstack[vm->dtop++] = - (fzl_value_t){.type = pow1.type, .as.u = pow_res}; - break; - - case FZL_OPCODE_MOD: - if (vm->dtop < 2) - { - snprintf(last_err, sizeof(last_err), - "runtime error - expected 2 values in stack"); - print_err_data(vm); - return -1; - } - fzl_value_t mod1 = vm->dstack[--vm->dtop]; - fzl_value_t mod2 = vm->dstack[--vm->dtop]; - vm->dstack[vm->dtop++] = - (fzl_value_t){.type = mod1.type, .as.u = mod2.as.u % mod1.as.u}; - break; - - case FZL_OPCODE_NEG: - if (vm->dtop < 1) - { - snprintf(last_err, sizeof(last_err), - "runtime error - expected value in stack"); - print_err_data(vm); - return -1; - } - fzl_value_t neg_n = vm->dstack[--vm->dtop]; - vm->dstack[vm->dtop++] = - (fzl_value_t){.type = neg_n.type, .as.u = -neg_n.as.u}; - break; - - case FZL_OPCODE_PUSHNUM: - { - uint64_t push_n = vm->ops[++vm->ip]; - vm->dstack[vm->dtop++] = - (fzl_value_t){.type = FZL_TYPE_UINT, .as.u = push_n}; - break; - } - - case FZL_OPCODE_PUSHBOOL: - { - uint64_t push_b = vm->ops[++vm->ip]; - vm->dstack[vm->dtop++] = - (fzl_value_t){.type = FZL_TYPE_BOOL, .as.b = push_b}; - break; - } - - default: - snprintf(last_err, sizeof(last_err), - "runtime error - unknown opcode %u", opcode); - print_err_data(vm); - return -1; + snprintf(last_err, sizeof(last_err), "runtime error - unknown opcode %u", + opcode); + print_err_data(vm); + return -1; } + opcode_handler handler = master_tbl[op_group][op_fnnum]; + result = handler(vm); + + if (result != 0) + return result; + vm->ip++; } diff --git a/src/vm/handlers/group_and_or.c b/src/vm/handlers/group_and_or.c new file mode 100644 index 0000000..a1febf2 --- /dev/null +++ b/src/vm/handlers/group_and_or.c @@ -0,0 +1,36 @@ +#include +#include + +int op_and(fzl_vm_t *vm) +{ + if (vm->dtop < 2) + { + snprintf(last_err, sizeof(last_err), + "runtime error - expected two values in data stack"); + print_err_data(vm); + return -1; + } + + fzl_value_t land = vm->dstack[--vm->dtop]; + fzl_value_t rand = vm->dstack[--vm->dtop]; + vm->dstack[vm->dtop++] = + (fzl_value_t){.type = FZL_TYPE_BOOL, .as.b = land.as.b && rand.as.b}; + return 0; +} + +int op_or(fzl_vm_t *vm) +{ + if (vm->dtop < 2) + { + snprintf(last_err, sizeof(last_err), + "runtime error - expected two values in data stack"); + print_err_data(vm); + return -1; + } + + fzl_value_t lor = vm->dstack[--vm->dtop]; + fzl_value_t ror = vm->dstack[--vm->dtop]; + vm->dstack[vm->dtop++] = + (fzl_value_t){.type = FZL_TYPE_BOOL, .as.b = lor.as.b || ror.as.b}; + return 0; +} diff --git a/src/vm/handlers/group_cmp.c b/src/vm/handlers/group_cmp.c new file mode 100644 index 0000000..672687e --- /dev/null +++ b/src/vm/handlers/group_cmp.c @@ -0,0 +1,104 @@ +#include +#include + +int op_cmp_eq(fzl_vm_t *vm) +{ + if (vm->dtop < 2) + { + snprintf(last_err, sizeof(last_err), + "runtime error - expected two values in data stack"); + print_err_data(vm); + return -1; + } + + fzl_value_t req = vm->dstack[--vm->dtop]; + fzl_value_t leq = vm->dstack[--vm->dtop]; + vm->dstack[vm->dtop++] = + (fzl_value_t){.type = FZL_TYPE_BOOL, .as.b = leq.as.u == req.as.u}; + return 0; +} + +int op_cmp_neq(fzl_vm_t *vm) +{ + if (vm->dtop < 2) + { + snprintf(last_err, sizeof(last_err), + "runtime error - expected two values in data stack"); + print_err_data(vm); + return -1; + } + + fzl_value_t rneq = vm->dstack[--vm->dtop]; + fzl_value_t lneq = vm->dstack[--vm->dtop]; + vm->dstack[vm->dtop++] = + (fzl_value_t){.type = FZL_TYPE_BOOL, .as.b = lneq.as.u != rneq.as.u}; + return 0; +} + +int op_cmp_lt(fzl_vm_t *vm) +{ + if (vm->dtop < 2) + { + snprintf(last_err, sizeof(last_err), + "runtime error - expected two values in data stack"); + print_err_data(vm); + return -1; + } + + fzl_value_t rlt = vm->dstack[--vm->dtop]; + fzl_value_t llt = vm->dstack[--vm->dtop]; + vm->dstack[vm->dtop++] = + (fzl_value_t){.type = FZL_TYPE_BOOL, .as.b = llt.as.u < rlt.as.u}; + return 0; +} + +int op_cmp_lteq(fzl_vm_t *vm) +{ + if (vm->dtop < 2) + { + snprintf(last_err, sizeof(last_err), + "runtime error - expected two values in data stack"); + print_err_data(vm); + return -1; + } + + fzl_value_t rlteq = vm->dstack[--vm->dtop]; + fzl_value_t llteq = vm->dstack[--vm->dtop]; + vm->dstack[vm->dtop++] = + (fzl_value_t){.type = FZL_TYPE_BOOL, .as.b = llteq.as.u <= rlteq.as.u}; + return 0; +} + +int op_cmp_gt(fzl_vm_t *vm) +{ + if (vm->dtop < 2) + { + snprintf(last_err, sizeof(last_err), + "runtime error - expected two values in data stack"); + print_err_data(vm); + return -1; + } + + fzl_value_t rgt = vm->dstack[--vm->dtop]; + fzl_value_t lgt = vm->dstack[--vm->dtop]; + vm->dstack[vm->dtop++] = + (fzl_value_t){.type = FZL_TYPE_BOOL, .as.b = lgt.as.u > rgt.as.u}; + return 0; +} + +int op_cmp_gteq(fzl_vm_t *vm) +{ + if (vm->dtop < 2) + { + snprintf(last_err, sizeof(last_err), + "runtime error - expected two values in data stack"); + print_err_data(vm); + return -1; + } + + fzl_value_t rgteq = vm->dstack[--vm->dtop]; + fzl_value_t lgteq = vm->dstack[--vm->dtop]; + vm->dstack[vm->dtop++] = + (fzl_value_t){.type = FZL_TYPE_BOOL, .as.b = lgteq.as.u >= rgteq.as.u}; + return 0; +} diff --git a/src/vm/handlers/group_ctx.c b/src/vm/handlers/group_ctx.c new file mode 100644 index 0000000..877c067 --- /dev/null +++ b/src/vm/handlers/group_ctx.c @@ -0,0 +1,127 @@ +#include +#include + +// Allocate environment table and increment scope level +int op_mk_envtable(fzl_vm_t *vm) +{ + vm->estack[vm->etop++] = fzl_mkenv(); + return 0; +} + +// Remove environment table and decrement scope level +int op_rm_envtable(fzl_vm_t *vm) +{ + fzl_freeenv(vm->estack[--vm->etop]); + return 0; +} + +// Store new variable +int op_store(fzl_vm_t *vm) +{ + if (vm->dtop < 1) + { + snprintf(last_err, sizeof(last_err), + "runtime error - expected var name in data stack"); + print_err_data(vm); + return -1; + } + + fzl_value_t varname = vm->dstack[--vm->dtop]; + fzl_env_storevalue(vm->estack[vm->etop - 1], (char *)varname.as.s.value, + (fzl_value_t){}); + + return 0; +} + +// Lookup environment(s) for variable using name on stack +int op_lookup_env(fzl_vm_t *vm) +{ + if (vm->dtop < 1) + { + snprintf(last_err, sizeof(last_err), + "runtime error - LOOKUP_ENV: empty data stack"); + print_err_data(vm); + return -1; + } + + fzl_value_t val = vm->dstack[--vm->dtop]; + fzl_value_t env_val; + bool result; + for (ssize_t i = vm->etop - 1; i >= 0; i--) + { + if ((result = + fzl_env_lookup(vm->estack[i], (char *)val.as.s.value, &env_val))) + break; + } + vm->dstack[vm->dtop++] = env_val; + + return 0; +} + +// Writable variable value +int op_write(fzl_vm_t *vm) +{ + if (vm->dtop < 2) + { + snprintf(last_err, sizeof(last_err), + "runtime error - expected 2 values in data stack"); + print_err_data(vm); + return -1; + } + + fzl_value_t writevar = vm->dstack[--vm->dtop]; + fzl_value_t value = vm->dstack[--vm->dtop]; + + char *wrname = (char *)writevar.as.s.value; + bool wrfound; + fzl_value_t wrenv; + ssize_t wrlvl = -1; + for (ssize_t i = vm->etop - 1; i >= 0; i--) + { + if ((wrfound = fzl_env_lookup(vm->estack[i], wrname, &wrenv))) + { + wrlvl = i; + break; + } + } + + if (wrlvl <= -1) + { + snprintf(last_err, sizeof(last_err), + "runtime error - name '%s' not found in this scope", wrname); + print_err_data(vm); + return -1; + } + + fzl_vm_env_wresult_t wresult = + fzl_env_writevalue(vm->estack[wrlvl], (char *)wrname, value); + if (wresult != FZL_VM_ENV_WRESULT_OK) + { + switch (wresult) + { + case FZL_VM_ENV_WRESULT_ERR: + snprintf(last_err, sizeof(last_err), + "runtime error - failed to write variable"); + print_err_data(vm); + return -1; + + case FZL_VM_ENV_WRESULT_ERR_EXISTS: + snprintf(last_err, sizeof(last_err), + "runtime error - redefenition of '%s'", writevar.as.s.value); + print_err_data(vm); + return -1; + + case FZL_VM_ENV_WRESULT_ERR_NOT_FOUND: + snprintf(last_err, sizeof(last_err), + "runtime error - cannot find name '%s'", writevar.as.s.value); + print_err_data(vm); + return -1; + + default: + print_err_data(vm); + return -1; + } + } + + return 0; +} diff --git a/src/vm/handlers/group_flow.c b/src/vm/handlers/group_flow.c new file mode 100644 index 0000000..0f1f02d --- /dev/null +++ b/src/vm/handlers/group_flow.c @@ -0,0 +1,54 @@ +#include +#include + +// Jump at specified address +int op_jmp(fzl_vm_t *vm) +{ + if (vm->ops_count <= vm->ip) + { + snprintf(last_err, sizeof(last_err), "runtime error - expected JMP addr"); + return -1; + } + + uint64_t jmp_ip = vm->ops[++vm->ip]; + if (!vm->allow_infinite) + { + if (jmp_ip == last_jmp_ip) + total_jmp_on_same++; + else + total_jmp_on_same = 0; + + if (total_jmp_on_same > vm->max_iter) + { + snprintf(last_err, sizeof(last_err), + "runtime error - exceeded max loop jumps (infinite loop?)\n" + "If you thinks there's mistake, change jumps limit with " + "--max-iter=N"); + return -1; + } + } + + vm->ip = jmp_ip - 1; + last_jmp_ip = jmp_ip; + + return 0; +} + +// Pop value from data stack and jump at specified address if value is zero +int op_jz(fzl_vm_t *vm) +{ + if (vm->dtop < 1) + { + snprintf(last_err, sizeof(last_err), + "runtime error - expected value in data stack"); + print_err_data(vm); + return -1; + } + + fzl_value_t jzval = vm->dstack[--vm->dtop]; + uint64_t jzip = vm->ops[++vm->ip] - 1; + if (jzval.as.b == 0) + vm->ip = jzip; + + return 0; +} diff --git a/src/vm/handlers/group_fn.c b/src/vm/handlers/group_fn.c new file mode 100644 index 0000000..72bb884 --- /dev/null +++ b/src/vm/handlers/group_fn.c @@ -0,0 +1,33 @@ +#include +#include + +// Call function (store return address and jump into function init) +int op_call(fzl_vm_t *vm) +{ + uint64_t ip = vm->ops[++vm->ip]; + vm->cstack[vm->ctop++] = vm->ip; + vm->ip = ip - 1; + return 0; +} + +// Pop return address from call stack and jump +int op_ret(fzl_vm_t *vm) +{ + if (vm->ctop < 1) + { + snprintf(last_err, sizeof(last_err), "runtime error - no place to jump"); + print_err_data(vm); + return -1; + } + + vm->ip = vm->cstack[--vm->ctop]; + return 0; +} + +// Call externally defined function +int op_call_extern(fzl_vm_t *vm) +{ + (void)vm; + // TODO: implement + return 0; +} diff --git a/src/vm/handlers/group_math.c b/src/vm/handlers/group_math.c new file mode 100644 index 0000000..d746366 --- /dev/null +++ b/src/vm/handlers/group_math.c @@ -0,0 +1,126 @@ +#include +#include + +// Pop two numbers and push sum result +int op_add(fzl_vm_t *vm) +{ + if (vm->dtop < 2) + { + snprintf(last_err, sizeof(last_err), + "runtime error - expected 2 values in stack"); + print_err_data(vm); + return -1; + } + fzl_value_t add1 = vm->dstack[--vm->dtop]; + fzl_value_t add2 = vm->dstack[--vm->dtop]; + vm->dstack[vm->dtop++] = + (fzl_value_t){.type = add1.type, .as.u = add1.as.u + add2.as.u}; + return 0; +} + +// Pop two numbers and push sum result +int op_sub(fzl_vm_t *vm) +{ + if (vm->dtop < 2) + { + snprintf(last_err, sizeof(last_err), + "runtime error - expected 2 values in stack"); + print_err_data(vm); + return -1; + } + fzl_value_t sub2 = vm->dstack[--vm->dtop]; + fzl_value_t sub1 = vm->dstack[--vm->dtop]; + vm->dstack[vm->dtop++] = + (fzl_value_t){.type = sub1.type, .as.u = sub1.as.u - sub2.as.u}; + return 0; +} + +// Pop two numbers and push sum result +int op_mul(fzl_vm_t *vm) +{ + if (vm->dtop < 2) + { + snprintf(last_err, sizeof(last_err), + "runtime error - expected 2 values in stack"); + print_err_data(vm); + return -1; + } + fzl_value_t mul1 = vm->dstack[--vm->dtop]; + fzl_value_t mul2 = vm->dstack[--vm->dtop]; + vm->dstack[vm->dtop++] = + (fzl_value_t){.type = mul1.type, .as.u = mul1.as.u * mul2.as.u}; + return 0; +} + +// Pop two numbers and push sum result +int op_div(fzl_vm_t *vm) +{ + if (vm->dtop < 2) + { + snprintf(last_err, sizeof(last_err), + "runtime error - expected 2 values in stack"); + print_err_data(vm); + return -1; + } + fzl_value_t div1 = vm->dstack[--vm->dtop]; + fzl_value_t div2 = vm->dstack[--vm->dtop]; + vm->dstack[vm->dtop++] = + (fzl_value_t){.type = div1.type, .as.u = div2.as.u / div1.as.u}; + return 0; +} + +// Pop two numbers and push sum result +int op_pow(fzl_vm_t *vm) +{ + if (vm->dtop < 2) + { + snprintf(last_err, sizeof(last_err), + "runtime error - expected 2 values in stack"); + print_err_data(vm); + return -1; + } + fzl_value_t pow2 = vm->dstack[--vm->dtop]; + fzl_value_t pow1 = vm->dstack[--vm->dtop]; + + uint64_t pow_n = pow1.as.u; + uint64_t pow_res = pow_n; + uint64_t pow_times = pow2.as.u; + for (size_t i = 1; i < pow_times; i++) + pow_res *= pow_n; + + vm->dstack[vm->dtop++] = (fzl_value_t){.type = pow1.type, .as.u = pow_res}; + return 0; +} + +// Pop two numbers and push sum result +int op_mod(fzl_vm_t *vm) +{ + if (vm->dtop < 2) + { + snprintf(last_err, sizeof(last_err), + "runtime error - expected 2 values in stack"); + print_err_data(vm); + return -1; + } + fzl_value_t mod1 = vm->dstack[--vm->dtop]; + fzl_value_t mod2 = vm->dstack[--vm->dtop]; + vm->dstack[vm->dtop++] = + (fzl_value_t){.type = mod1.type, .as.u = mod2.as.u % mod1.as.u}; + return 0; +} + +// Pop two numbers and push sum result +int op_neg(fzl_vm_t *vm) +{ + if (vm->dtop < 1) + { + snprintf(last_err, sizeof(last_err), + "runtime error - expected value in stack"); + print_err_data(vm); + return -1; + } + fzl_value_t neg_n = vm->dstack[--vm->dtop]; + vm->dstack[vm->dtop++] = + (fzl_value_t){.type = neg_n.type, .as.u = -neg_n.as.u}; + return 0; +} diff --git a/src/vm/handlers/group_not.c b/src/vm/handlers/group_not.c new file mode 100644 index 0000000..ab88558 --- /dev/null +++ b/src/vm/handlers/group_not.c @@ -0,0 +1,18 @@ +#include +#include + +int op_not(fzl_vm_t *vm) +{ + if (vm->dtop < 1) + { + snprintf(last_err, sizeof(last_err), + "runtime error - expected value in data stack"); + print_err_data(vm); + return -1; + } + + fzl_value_t notval = vm->dstack[--vm->dtop]; + vm->dstack[vm->dtop++] = + (fzl_value_t){.type = FZL_TYPE_BOOL, .as.b = !notval.as.b}; + return 0; +} diff --git a/src/vm/handlers/group_obj.c b/src/vm/handlers/group_obj.c new file mode 100644 index 0000000..ae02cb8 --- /dev/null +++ b/src/vm/handlers/group_obj.c @@ -0,0 +1,223 @@ +#include +#include +#include +#include + +// Escape objects with nested objects check +static void escape_recursive(fzl_vm_env_t *current, fzl_vm_env_t *parent, + fzl_vm_object_t *obj) +{ + if (!current || !parent || !obj) + return; + + bool found_in_current = false; + for (size_t i = 0; i < current->owned_objects_count; i++) + { + if (current->owned_objects[i] == obj) + { + current->owned_objects[i] = NULL; + found_in_current = true; + break; + } + } + + if (found_in_current) + { + bool found_in_parent = false; + for (size_t i = 0; i < parent->owned_objects_count; i++) + { + if (parent->owned_objects[i] == obj) + { + found_in_parent = true; + break; + } + } + + if (!found_in_parent) + { + if (parent->owned_objects_remainder <= parent->owned_objects_count) + { + parent->owned_objects_remainder *= 2; + parent->owned_objects = + realloc(parent->owned_objects, parent->owned_objects_remainder * + sizeof(fzl_vm_object_t *)); + } + + parent->owned_objects[parent->owned_objects_count++] = obj; + } + } + + for (size_t i = 0; i < obj->values_count; i++) + { + if (obj->values[i].type == FZL_TYPE_OBJECT && obj->values[i].as.obj != NULL) + escape_recursive(current, parent, obj->values[i].as.obj); + } +} + +// Allocate specified count of fields and create object +int op_alloc_object(fzl_vm_t *vm) +{ + uint64_t values_count = vm->ops[++vm->ip]; + fzl_vm_object_t *obj = malloc(sizeof(fzl_vm_object_t)); + + if (values_count > 0) + obj->values = malloc(values_count * sizeof(fzl_value_t)); + obj->values_count = values_count; + memset(obj->values, 0, values_count * sizeof(fzl_value_t)); + + fzl_vm_env_t *current_env = vm->estack[vm->etop - 1]; + if (current_env->owned_objects_remainder <= current_env->owned_objects_count) + { + current_env->owned_objects_remainder *= 2; + current_env->owned_objects = realloc(current_env->owned_objects, + current_env->owned_objects_remainder * + sizeof(fzl_vm_object_t *)); + } + + current_env->owned_objects[current_env->owned_objects_count++] = obj; + vm->dstack[vm->dtop++] = + (fzl_value_t){.type = FZL_TYPE_OBJECT, .as.obj = obj}; + + return 0; +} + +// Pop object from stack and push its field value +int op_get_field(fzl_vm_t *vm) +{ + if (vm->dtop < 1) + { + snprintf(last_err, sizeof(last_err), + "runtime error - expected value in data stack"); + print_err_data(vm); + return -1; + } + + uint64_t field_slot = vm->ops[++vm->ip]; + fzl_value_t val = vm->dstack[--vm->dtop]; + if (val.type != FZL_TYPE_OBJECT) + { + snprintf(last_err, sizeof(last_err), + "runtime error - cannot access field #%lu of non-object type", + field_slot); + print_err_data(vm); + return -1; + } + + fzl_vm_object_t *obj = val.as.obj; + if (obj->values_count <= field_slot) + { + snprintf(last_err, sizeof(last_err), + "runtime error - cannot access field #%lu", field_slot); + print_err_data(vm); + return -1; + } + + vm->dstack[vm->dtop++] = obj->values[field_slot]; + + return 0; +} + +// Pop object and write value from stack and write value into objects' field +int op_set_field(fzl_vm_t *vm) +{ + if (vm->dtop < 2) + { + snprintf(last_err, sizeof(last_err), + "runtime error - expected two values in data stack"); + print_err_data(vm); + return -1; + } + + uint64_t field_slot = vm->ops[++vm->ip]; + fzl_value_t objval = vm->dstack[--vm->dtop]; + fzl_value_t wrval = vm->dstack[--vm->dtop]; + if (objval.type != FZL_TYPE_OBJECT) + { + snprintf(last_err, sizeof(last_err), + "runtime error - cannot access field #%lu of non-object type", + field_slot); + print_err_data(vm); + return -1; + } + + fzl_vm_object_t *obj = objval.as.obj; + if (obj->values_count <= field_slot) + { + snprintf(last_err, sizeof(last_err), + "runtime error - cannot access field #%lu", field_slot); + print_err_data(vm); + return -1; + } + + obj->values[field_slot] = wrval; + + return 0; +} + +// Escape object to the parent scope +int op_escape_object(fzl_vm_t *vm) +{ + if (vm->dtop < 1) + { + snprintf(last_err, sizeof(last_err), + "runtime error - missing value in data stack"); + print_err_data(vm); + return -1; + } + fzl_value_t obj = vm->dstack[--vm->dtop]; + if (obj.type != FZL_TYPE_OBJECT) + { + snprintf(last_err, sizeof(last_err), + "runtime error - cannot escape non-object type"); + print_err_data(vm); + return -1; + } + + fzl_vm_env_t *parent = vm->estack[vm->etop - 2]; + fzl_vm_env_t *current = vm->estack[vm->etop - 1]; + if (vm->etop < 2) + { + snprintf(last_err, sizeof(last_err), + "runtime error - cannot escape object from global env"); + print_err_data(vm); + return -1; + } + + escape_recursive(current, parent, obj.as.obj); + vm->dstack[vm->dtop++] = obj; + + return 0; +} + +// Pop value from data stack and store dereferenced value +int op_dereference(fzl_vm_t *vm) +{ + if (vm->dtop < 1) + { + snprintf(last_err, sizeof(last_err), + "runtime error - expected value in data stack"); + print_err_data(vm); + return -1; + } + + fzl_value_t addr = vm->dstack[--vm->dtop]; + if (addr.type < FZL_TYPE_INT8) + { + snprintf(last_err, sizeof(last_err), "runtime error - expected address"); + print_err_data(vm); + return -1; + } + + if (addr.as.u == 0) + { + vm->dstack[vm->dtop++] = + (fzl_value_t){.type = FZL_TYPE_OBJECT, .as.obj = NULL}; + } + else + { + vm->dstack[vm->dtop++] = (fzl_value_t){ + .type = FZL_TYPE_OBJECT, .as.obj = (fzl_vm_object_t *)addr.as.u}; + } + + return 0; +} diff --git a/src/vm/handlers/group_objcmp.c b/src/vm/handlers/group_objcmp.c new file mode 100644 index 0000000..671bcbd --- /dev/null +++ b/src/vm/handlers/group_objcmp.c @@ -0,0 +1,36 @@ +#include +#include + +int op_cmp_eq_obj(fzl_vm_t *vm) +{ + if (vm->dtop < 2) + { + snprintf(last_err, sizeof(last_err), + "runtime error - expected two values in data stack"); + print_err_data(vm); + return -1; + } + fzl_value_t left = vm->dstack[--vm->dtop]; + fzl_value_t right = vm->dstack[--vm->dtop]; + + vm->dstack[vm->dtop++] = + (fzl_value_t){.type = FZL_TYPE_BOOL, .as.b = left.as.obj == right.as.obj}; + return 0; +} + +int op_cmp_neq_obj(fzl_vm_t *vm) +{ + if (vm->dtop < 2) + { + snprintf(last_err, sizeof(last_err), + "runtime error - expected two values in data stack"); + print_err_data(vm); + return -1; + } + fzl_value_t left = vm->dstack[--vm->dtop]; + fzl_value_t right = vm->dstack[--vm->dtop]; + + vm->dstack[vm->dtop++] = + (fzl_value_t){.type = FZL_TYPE_BOOL, .as.b = left.as.obj != right.as.obj}; + return 0; +} diff --git a/src/vm/handlers/group_stack.c b/src/vm/handlers/group_stack.c new file mode 100644 index 0000000..4b60bd8 --- /dev/null +++ b/src/vm/handlers/group_stack.c @@ -0,0 +1,78 @@ +#include +#include +#include + +// Load constant onto stack +int op_loadconst(fzl_vm_t *vm) +{ + uint64_t cn = vm->ops[++vm->ip]; + if (vm->consts_count <= cn) + { + snprintf(last_err, sizeof(last_err), "runtime error - unknown const"); + print_err_data(vm); + return -1; + } + fzl_const_t c = vm->consts[cn]; + vm->dstack[vm->dtop++] = (fzl_value_t){.type = FZL_TYPE_STRING, + .as.s.length = c.as.string.length, + .as.s.value = c.as.string.chars}; + + return 0; +} + +// Call builtin function +int op_call_builtin(fzl_vm_t *vm) +{ + uint64_t fn_id = vm->ops[++vm->ip]; + if (fn_id == FZL_BUILTIN_PRINTLINE) + { + fzl_value_t arg = vm->dstack[--vm->dtop]; + if (arg.type == FZL_TYPE_STRING) + printf("%s\n", arg.as.s.value); + else if (arg.type == FZL_TYPE_CHAR) + printf("%c\n", arg.as.c); + else if (arg.type == FZL_TYPE_INT) + printf("%ld\n", arg.as.i); + else if (arg.type == FZL_TYPE_UINT) + printf("%lu\n", arg.as.u); + else if (arg.type == FZL_TYPE_BOOL) + printf("%s\n", arg.as.b ? "true" : "false"); + else if (arg.type == FZL_TYPE_NULL) + printf("\n"); + else if (arg.type == FZL_TYPE_OBJECT) + { + print_object(arg.as.obj); + printf("\n"); + } + } + else if (fn_id == FZL_BUILTIN_EXIT) + { + fzl_value_t arg = vm->dstack[--vm->dtop]; + return arg.as.i; + } + else + { + snprintf(last_err, sizeof(last_err), + "runtime error - undefined builtin %lu", fn_id); + print_err_data(vm); + return -1; + } + + return 0; +} + +// Push number onto stack +int op_pushnum(fzl_vm_t *vm) +{ + uint64_t push_n = vm->ops[++vm->ip]; + vm->dstack[vm->dtop++] = (fzl_value_t){.type = FZL_TYPE_UINT, .as.u = push_n}; + return 0; +} + +// Push boolean onto stack +int op_pushbool(fzl_vm_t *vm) +{ + uint64_t push_b = vm->ops[++vm->ip]; + vm->dstack[vm->dtop++] = (fzl_value_t){.type = FZL_TYPE_BOOL, .as.b = push_b}; + return 0; +} diff --git a/src/vm/handlers/group_str.c b/src/vm/handlers/group_str.c new file mode 100644 index 0000000..9d93c01 --- /dev/null +++ b/src/vm/handlers/group_str.c @@ -0,0 +1,137 @@ +#include +#include +#include + +char build_result[512]; + +// Build string from specified count using stack values +int op_build_str(fzl_vm_t *vm) +{ + uint64_t n = vm->ops[++vm->ip]; + if (vm->dtop < n) + { + snprintf(last_err, sizeof(last_err), + "runtime error - expected %zu value(s) in dstack", n); + print_err_data(vm); + return -1; + } + + char temp[64]; + size_t gi = 0; + memset(build_result, 0, sizeof(build_result)); + for (size_t i = 0; i < n; i++) + { + if (gi >= 512) + { + for (size_t i = 511; i > 508; i--) + build_result[i] = '.'; + break; + } + + memset(temp, 0, sizeof(temp)); + fzl_value_t val = vm->dstack[i]; + + switch (val.type) + { + case FZL_TYPE_UINT: + snprintf(temp, sizeof(temp), "%zu", val.as.u); + strcpy(build_result + gi, temp); + gi += strlen(temp); + break; + + case FZL_TYPE_UINT64: + snprintf(temp, sizeof(temp), "%lu", val.as.u64); + strcpy(build_result + gi, temp); + gi += strlen(temp); + break; + + case FZL_TYPE_UINT32: + snprintf(temp, sizeof(temp), "%u", val.as.u32); + strcpy(build_result + gi, temp); + gi += strlen(temp); + break; + + case FZL_TYPE_UINT16: + snprintf(temp, sizeof(temp), "%u", val.as.u16); + strcpy(build_result + gi, temp); + gi += strlen(temp); + break; + + case FZL_TYPE_UINT8: + snprintf(temp, sizeof(temp), "%u", val.as.u8); + strcpy(build_result + gi, temp); + gi += strlen(temp); + break; + + case FZL_TYPE_INT: + snprintf(temp, sizeof(temp), "%zd", val.as.i); + strcpy(build_result + gi, temp); + gi += strlen(temp); + break; + + case FZL_TYPE_INT64: + snprintf(temp, sizeof(temp), "%ld", val.as.i64); + strcpy(build_result + gi, temp); + gi += strlen(temp); + break; + + case FZL_TYPE_INT32: + snprintf(temp, sizeof(temp), "%d", val.as.i32); + strcpy(build_result + gi, temp); + gi += strlen(temp); + break; + + case FZL_TYPE_INT16: + snprintf(temp, sizeof(temp), "%d", val.as.i16); + strcpy(build_result + gi, temp); + gi += strlen(temp); + break; + + case FZL_TYPE_INT8: + snprintf(temp, sizeof(temp), "%d", val.as.i8); + strcpy(build_result + gi, temp); + gi += strlen(temp); + break; + + case FZL_TYPE_BOOL: + snprintf(build_result + gi, sizeof(build_result) - gi, "%s", + val.as.b ? "true" : "false"); + gi += val.as.b ? 4 : 5; + break; + + case FZL_TYPE_CHAR: + snprintf(build_result + gi, sizeof(build_result) - gi, "%d", val.as.c); + gi += 1; + break; + + case FZL_TYPE_STRING: + snprintf(build_result + gi, sizeof(build_result) - gi, "%s", + val.as.s.value); + gi += val.as.s.length; + break; + + case FZL_TYPE_EXTERN_FN: + snprintf(temp, sizeof(temp), "", val.as.u); + strcpy(build_result + gi, temp); + gi += strlen(temp); + break; + + case FZL_TYPE_OBJECT: + snprintf(temp, sizeof(temp), "", val.as.obj); + strcpy(build_result + gi, temp); + gi += strlen(temp); + break; + + case FZL_TYPE_NULL: + snprintf(build_result + gi, sizeof(build_result) - gi, ""); + gi += 6; + break; + } + } + + vm->dtop -= n; + vm->dstack[vm->dtop++] = (fzl_value_t){.type = FZL_TYPE_STRING, + .as.s.length = strlen(build_result), + .as.s.value = build_result}; + return 0; +} diff --git a/src/vm/handlers/group_strcmp.c b/src/vm/handlers/group_strcmp.c new file mode 100644 index 0000000..50dffb1 --- /dev/null +++ b/src/vm/handlers/group_strcmp.c @@ -0,0 +1,39 @@ +#include +#include +#include + +int op_cmp_eq_str(fzl_vm_t *vm) +{ + if (vm->dtop < 2) + { + snprintf(last_err, sizeof(last_err), + "runtime error - expected two values in data stack"); + print_err_data(vm); + return -1; + } + fzl_value_t left = vm->dstack[--vm->dtop]; + fzl_value_t right = vm->dstack[--vm->dtop]; + + int result = strcmp(left.as.s.value, right.as.s.value); + vm->dstack[vm->dtop++] = + (fzl_value_t){.type = FZL_TYPE_BOOL, .as.b = result == 0}; + return 0; +} + +int op_cmp_neq_str(fzl_vm_t *vm) +{ + if (vm->dtop < 2) + { + snprintf(last_err, sizeof(last_err), + "runtime error - expected two values in data stack"); + print_err_data(vm); + return -1; + } + fzl_value_t left = vm->dstack[--vm->dtop]; + fzl_value_t right = vm->dstack[--vm->dtop]; + + int result = strcmp(left.as.s.value, right.as.s.value); + vm->dstack[vm->dtop++] = + (fzl_value_t){.type = FZL_TYPE_BOOL, .as.b = result != 0}; + return 0; +} diff --git a/src/vm/handlers/master.c b/src/vm/handlers/master.c new file mode 100644 index 0000000..60d6f12 --- /dev/null +++ b/src/vm/handlers/master.c @@ -0,0 +1,81 @@ +#include + +// Stack-relative opcodes group +const opcode_handler group_stack[] = { + [1] = op_loadconst, + [2] = op_call_builtin, + [3] = op_pushnum, + [4] = op_pushbool, +}; + +// Context-relative opcodes group +const opcode_handler group_ctx[] = { + [1] = op_mk_envtable, [2] = op_rm_envtable, [3] = op_store, + [4] = op_lookup_env, [5] = op_write, +}; + +// Flow-relative opcodes group +const opcode_handler group_flow[] = { + [1] = op_jmp, + [2] = op_jz, +}; + +// Function-relative opcodes group +const opcode_handler group_fn[] = { + [1] = op_call, + [2] = op_ret, +}; + +// Object-relative opcodes group +const opcode_handler group_obj[] = { + [1] = op_alloc_object, [2] = op_get_field, [3] = op_set_field, + [4] = op_escape_object, [5] = op_dereference, +}; + +// Math-relative opcodes group +const opcode_handler group_math[] = { + [1] = op_add, [2] = op_sub, [3] = op_mul, [4] = op_div, + [5] = op_pow, [6] = op_mod, [7] = op_neg, +}; + +// String-relative opcodes group +const opcode_handler group_str[] = { + [1] = op_build_str, +}; + +// Basic comparison +const opcode_handler group_cmp[] = { + [1] = op_cmp_eq, [2] = op_cmp_neq, [3] = op_cmp_lt, + [4] = op_cmp_lteq, [5] = op_cmp_gt, [6] = op_cmp_gteq, +}; + +// Strings comparison +const opcode_handler group_strcmp[] = { + [1] = op_cmp_eq_str, + [2] = op_cmp_neq_str, +}; + +// Object comparison +const opcode_handler group_objcmp[] = { + [1] = op_cmp_eq_obj, + [2] = op_cmp_neq_obj, +}; + +// AND / OR opcodes +const opcode_handler group_and_or[] = { + [1] = op_and, + [2] = op_or, +}; + +// NOT opcode +const opcode_handler group_not[] = { + [1] = op_not, +}; + +// Master table +const opcode_handler *const master_tbl[] = { + [0xF0] = group_stack, [0xF1] = group_ctx, [0xF2] = group_flow, + [0xF3] = group_fn, [0xFA] = group_obj, [0xA0] = group_math, + [0xA1] = group_str, [0xB0] = group_cmp, [0xB1] = group_strcmp, + [0xB2] = group_objcmp, [0xC0] = group_and_or, [0xD0] = group_not, +}; diff --git a/src/vm/main.c b/src/vm/main.c index 62dbef8..b8f2530 100644 --- a/src/vm/main.c +++ b/src/vm/main.c @@ -121,11 +121,14 @@ int main(int argc, char **argv) return -1; } + vm->allow_infinite = args->allow_infinite; + vm->max_iter = args->max_iter; + if (args->debug) fzl_vm_print_ops(vm); else { - int result = fzl_vm_run(vm, args->allow_infinite, args->max_iter); + int result = fzl_vm_run(vm); if (result != 0) { char *err = fzl_vm_geterror();