feat: implement vm

This commit is contained in:
samsonium
2026-10-10 05:38:16 +03:00
parent 96115b347e
commit cdf929dfc4
4 changed files with 315 additions and 3 deletions
+183
View File
@@ -0,0 +1,183 @@
#include "common/fzl_builtins.h"
#include "common/fzl_opcodes.h"
#include "compiler/utils/fzl_const_pool.h"
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <vm/fzl_vm.h>
char last_err[256];
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;
memset(vm->stack, 0, sizeof(vm->stack));
vm->stack_top = 0;
memset(vm->arg_stack, 0, sizeof(vm->arg_stack));
vm->arg_top = 0;
FILE *fd = fopen(filepath, "rb");
if (!fd)
{
snprintf(last_err, sizeof(last_err), "failed to open file '%s'", filepath);
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");
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)");
return NULL;
}
if (consts_count > 0)
{
vm->consts_count = consts_count;
vm->consts = malloc(consts_count * sizeof(fzl_const_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
{
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;
}
return vm;
}
int fzl_vm_run(fzl_vm_t *vm)
{
if (!vm)
{
snprintf(last_err, sizeof(last_err), "vm is null");
return -1;
}
vm->ip = 0;
while (vm->ip < vm->ops_count)
{
uint64_t opcode = vm->ops[vm->ip];
switch (opcode)
{
case FZL_OPCODE_CALL_BUILTIN:
uint64_t fn_id = vm->ops[++vm->ip];
if (fn_id == FZL_BUILTIN_PRINTLINE)
{
fzl_const_t arg = vm->arg_stack[--vm->arg_top];
printf("%s\n", arg.as.string.chars);
}
else
{
snprintf(last_err, sizeof(last_err),
"runtime error - undefined builtin %lu", fn_id);
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");
return -1;
}
fzl_const_t c = vm->consts[cn];
vm->stack[vm->stack_top++] = c;
break;
case FZL_OPCODE_PUSHARG:
if (vm->stack_top < 1)
{
snprintf(last_err, sizeof(last_err),
"runtime error - cannot push argument: stack is empty");
return -1;
}
vm->arg_stack[vm->arg_top++] = vm->stack[--vm->stack_top];
break;
default:
snprintf(last_err, sizeof(last_err), "runtime error - unknown opcode %lu",
opcode);
return -1;
}
vm->ip++;
}
return 0;
}
char *fzl_vm_geterror() { return last_err; }
void fzl_freevm(fzl_vm_t *vm)
{
if (!vm)
return;
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);
}