feat: optimize VM using dispatch table

This commit is contained in:
samsonium
2026-10-29 02:59:38 +03:00
parent e2de23ed57
commit f4c97795fa
18 changed files with 1240 additions and 938 deletions
+13
View File
@@ -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
+5 -1
View File
@@ -3,6 +3,7 @@
#include <common/fzl_value.h>
#include <compiler/utils/fzl_const_pool.h> // NOTE: move outside compiler scope to common
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <vm/fzl_vm_env.h>
@@ -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();
+108
View File
@@ -0,0 +1,108 @@
#ifndef INTERNALS_OPCODE_H
#define INTERNALS_OPCODE_H
#include "vm/fzl_vm.h"
#include <stddef.h>
// 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
+18 -936
View File
@@ -12,8 +12,8 @@
#include <vm/fzl_vm.h>
#include <vm/fzl_vm_env.h>
#include <vm/fzl_vm_object.h>
#include <vm/internals/opcode.h>
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), "<extern:%zu>", val.as.u);
strcpy(build_result + gi, temp);
gi += strlen(temp);
break;
case FZL_TYPE_OBJECT:
snprintf(temp, sizeof(temp), "<ref:%p>", val.as.obj);
strcpy(build_result + gi, temp);
gi += strlen(temp);
break;
case FZL_TYPE_NULL:
snprintf(build_result + gi, sizeof(build_result) - gi, "<null>");
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("<null>\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++;
}
+36
View File
@@ -0,0 +1,36 @@
#include <vm/fzl_vm.h>
#include <vm/internals/opcode.h>
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;
}
+104
View File
@@ -0,0 +1,104 @@
#include <vm/fzl_vm.h>
#include <vm/internals/opcode.h>
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;
}
+127
View File
@@ -0,0 +1,127 @@
#include <vm/fzl_vm.h>
#include <vm/internals/opcode.h>
// 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;
}
+54
View File
@@ -0,0 +1,54 @@
#include <vm/fzl_vm.h>
#include <vm/internals/opcode.h>
// 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;
}
+33
View File
@@ -0,0 +1,33 @@
#include <vm/fzl_vm.h>
#include <vm/internals/opcode.h>
// 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;
}
+126
View File
@@ -0,0 +1,126 @@
#include <vm/fzl_vm.h>
#include <vm/internals/opcode.h>
// 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;
}
+18
View File
@@ -0,0 +1,18 @@
#include <vm/fzl_vm.h>
#include <vm/internals/opcode.h>
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;
}
+223
View File
@@ -0,0 +1,223 @@
#include <stdlib.h>
#include <string.h>
#include <vm/fzl_vm.h>
#include <vm/internals/opcode.h>
// 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;
}
+36
View File
@@ -0,0 +1,36 @@
#include <vm/fzl_vm.h>
#include <vm/internals/opcode.h>
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;
}
+78
View File
@@ -0,0 +1,78 @@
#include <common/fzl_builtins.h>
#include <vm/fzl_vm.h>
#include <vm/internals/opcode.h>
// 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("<null>\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;
}
+137
View File
@@ -0,0 +1,137 @@
#include <string.h>
#include <vm/fzl_vm.h>
#include <vm/internals/opcode.h>
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), "<extern:%zu>", val.as.u);
strcpy(build_result + gi, temp);
gi += strlen(temp);
break;
case FZL_TYPE_OBJECT:
snprintf(temp, sizeof(temp), "<ref:%p>", val.as.obj);
strcpy(build_result + gi, temp);
gi += strlen(temp);
break;
case FZL_TYPE_NULL:
snprintf(build_result + gi, sizeof(build_result) - gi, "<null>");
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;
}
+39
View File
@@ -0,0 +1,39 @@
#include <string.h>
#include <vm/fzl_vm.h>
#include <vm/internals/opcode.h>
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;
}
+81
View File
@@ -0,0 +1,81 @@
#include <vm/internals/opcode.h>
// 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,
};
+4 -1
View File
@@ -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();