#include #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; } memset(vm, 0, sizeof(fzl_vm_t)); 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); // NOTE: add red bytes check (also for freads below) 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); // TODO: read externs count 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; } // TODO: read externs fclose(fd); return vm; } 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) { if (!vm) { snprintf(last_err, sizeof(last_err), "vm is null"); return -1; } 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) & 0xFF; size_t op_fnnum = (opcode & 0xFF); if (master_tbl[op_group] == NULL || master_tbl[op_group][op_fnnum] == NULL) { 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++; } 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); }