#include #include #include #include #include #include #include #include #include #include #include #include #include #include char last_err[256]; size_t last_jmp_ip = 0; size_t total_jmp_on_same = 0; static char *expect_hdr = "FZL\0\1"; fzl_vm_t *fzl_mkvm(const char *filepath) { fzl_vm_t *vm = malloc(sizeof(fzl_vm_t)); if (!vm) { snprintf(last_err, sizeof(last_err), "failed to allocate vm"); return NULL; } vm->consts = NULL; vm->consts_count = 0; vm->ops = NULL; vm->ops_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)); FILE *fd = fopen(filepath, "rb"); if (!fd) { snprintf(last_err, sizeof(last_err), "failed to open file '%s'", filepath); free(vm); return NULL; } char hdr[5]; fread(hdr, sizeof(char), 5, fd); if (memcmp(hdr, expect_hdr, 5) != 0) { snprintf(last_err, sizeof(last_err), "magic check error"); fclose(fd); free(vm); return NULL; } size_t consts_count, ops_count; fread(&consts_count, sizeof(size_t), 1, fd); fread(&ops_count, sizeof(size_t), 1, fd); if (ops_count < 1) { snprintf(last_err, sizeof(last_err), "there is nothing to do (0 ops)"); fclose(fd); free(vm); return NULL; } if (consts_count > 0) { vm->consts_count = consts_count; vm->consts = malloc(consts_count * sizeof(fzl_value_t)); for (size_t i = 0; i < consts_count; i++) { fzl_consttype_t type; fread(&type, sizeof(fzl_consttype_t), 1, fd); if (type == FZL_CONSTTYPE_STRING) { size_t length; char *chars; fread(&length, sizeof(size_t), 1, fd); chars = malloc(length * sizeof(char) + 1); fread(chars, sizeof(char), length, fd); chars[length] = 0; vm->consts[i] = (fzl_const_t){ .type = type, .as.string.length = length, .as.string.chars = chars}; } else { fclose(fd); free(vm->consts); free(vm); snprintf(last_err, sizeof(last_err), "unknown const type at index %ld", i); return NULL; } } } vm->ops_count = ops_count; vm->ops = malloc(ops_count * sizeof(uint64_t)); for (size_t i = 0; i < ops_count; i++) { uint64_t op; fread(&op, sizeof(uint64_t), 1, fd); vm->ops[i] = op; } fclose(fd); 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) { printf("\n"); return; } printf("obj<"); for (size_t i = 0; i < obj->values_count; i++) { fzl_value_t arg = obj->values[i]; if (i > 0) printf(", "); if (arg.type == FZL_TYPE_STRING) printf("str(%s)", arg.as.s.value); else if (arg.type == FZL_TYPE_CHAR) printf("char(%c)", arg.as.c); else if (arg.type == FZL_TYPE_INT) printf("int(%ld)", arg.as.i); else if (arg.type == FZL_TYPE_UINT) printf("uint(%lu)", arg.as.u); else if (arg.type == FZL_TYPE_BOOL) printf("bool(%s)", arg.as.b ? "true" : "false"); else if (arg.type == FZL_TYPE_NULL) printf(""); else if (arg.type == FZL_TYPE_OBJECT) print_object(arg.as.obj); else printf(""); } printf(">"); } void print_err_data(fzl_vm_t *vm) { printf("===== ERROR =====\n"); printf("> Caused on IP: %zu\n", vm->ip); printf("\n> Data stack:\n"); if (vm->dtop > 0) { for (ssize_t i = vm->dtop - 1; i >= 0; i--) { printf("\t> [%d] ", vm->dstack[i].type); switch (vm->dstack[i].type) { case FZL_TYPE_OBJECT: printf("ref: %p\n", vm->dstack[i].as.obj); break; case FZL_TYPE_STRING: printf("str: '%s'\n", vm->dstack[i].as.s.value); break; case FZL_TYPE_NULL: printf("\n"); break; default: printf("%lu\n", vm->dstack[i].as.u); break; } } } else { printf("\tEmpty\n"); } printf("\n> Environments:\n"); for (ssize_t i = vm->etop - 1; i >= 0; i--) { fzl_vm_env_t *env = vm->estack[i]; printf("\t> [%zd] %p:\n", i, env); printf("\t\t> Owned objects (%zu/%zu):\n", env->owned_objects_count, env->owned_objects_remainder); for (size_t j = 0; j < env->owned_objects_count; j++) printf("\t\t\t> ref: %p (entries: %zu)\n", env->owned_objects[j], env->owned_objects[j]->values_count); printf("\t\t> Local entries (%zu/%zu):\n", env->size, env->remainder); for (size_t j = 0; j < env->size; j++) { switch (env->table[j].value.type) { case FZL_TYPE_OBJECT: printf("\t\t\t> [%d] %s = ref %p\n", env->table[j].value.type, env->table[j].name, env->table[j].value.as.obj); break; default: printf("\t\t\t> [%d] %s = %lu\n", env->table[j].value.type, env->table[j].name, env->table[j].value.as.u); break; } } } printf("\n> Call stack:\n"); for (ssize_t i = vm->ctop - 1; i >= 0; i--) printf("\t> [%zd] %lu\n", i, vm->cstack[i]); } int fzl_vm_run(fzl_vm_t *vm, bool allow_infinite, uint64_t max_iter) { if (!vm) { snprintf(last_err, sizeof(last_err), "vm is null"); return -1; } vm->ip = 0; while (vm->ip < vm->ops_count) { fzl_opcode_t opcode = vm->ops[vm->ip]; switch (opcode) { 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)); 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; } vm->ip++; } return 0; } char *fzl_vm_geterror() { return last_err; } void fzl_freevm(fzl_vm_t *vm) { if (!vm) return; while (vm->etop > 0) fzl_freeenv(vm->estack[--vm->etop]); for (size_t i = 0; i < vm->consts_count; i++) { if (vm->consts[i].type == FZL_CONSTTYPE_STRING) free((void *)vm->consts[i].as.string.chars); } free(vm->consts); free(vm->ops); free(vm); }