L2_VAL_TYPE_NAMESPACE, | L2_VAL_TYPE_NAMESPACE, | ||||
L2_VAL_TYPE_FUNCTION, | L2_VAL_TYPE_FUNCTION, | ||||
L2_VAL_TYPE_CFUNCTION, | L2_VAL_TYPE_CFUNCTION, | ||||
L2_VAL_TYPE_ERROR, | |||||
}; | }; | ||||
enum l2_value_flags { | enum l2_value_flags { | ||||
l2_word ns; | l2_word ns; | ||||
} func; | } func; | ||||
l2_vm_cfunction cfunc; | l2_vm_cfunction cfunc; | ||||
char *error; | |||||
}; | }; | ||||
}; | }; | ||||
void l2_vm_init(struct l2_vm *vm, l2_word *ops, size_t opcount); | void l2_vm_init(struct l2_vm *vm, l2_word *ops, size_t opcount); | ||||
l2_word l2_vm_alloc(struct l2_vm *vm, enum l2_value_type typ, enum l2_value_flags flags); | l2_word l2_vm_alloc(struct l2_vm *vm, enum l2_value_type typ, enum l2_value_flags flags); | ||||
l2_word l2_vm_error(struct l2_vm *vm, const char *fmt, ...); | |||||
void l2_vm_free(struct l2_vm *vm); | void l2_vm_free(struct l2_vm *vm); | ||||
void l2_vm_step(struct l2_vm *vm); | void l2_vm_step(struct l2_vm *vm); | ||||
void l2_vm_run(struct l2_vm *vm); | void l2_vm_run(struct l2_vm *vm); |
case L2_VAL_TYPE_CFUNCTION: | case L2_VAL_TYPE_CFUNCTION: | ||||
l2_io_printf(out, "(function)"); | l2_io_printf(out, "(function)"); | ||||
break; | break; | ||||
case L2_VAL_TYPE_ERROR: | |||||
l2_io_printf(out, "(error: %s)", val->error); | |||||
break; | |||||
} | } | ||||
} | } | ||||
case L2_VAL_TYPE_REAL: | case L2_VAL_TYPE_REAL: | ||||
case L2_VAL_TYPE_FUNCTION: | case L2_VAL_TYPE_FUNCTION: | ||||
case L2_VAL_TYPE_CFUNCTION: | case L2_VAL_TYPE_CFUNCTION: | ||||
case L2_VAL_TYPE_ERROR: | |||||
break; | break; | ||||
case L2_VAL_TYPE_BUFFER: | case L2_VAL_TYPE_BUFFER: |
// ISO C doesn't let you cast a function pointer to void*. | // ISO C doesn't let you cast a function pointer to void*. | ||||
printf("C FUNCTION, %jx\n", (uintmax_t)val->cfunc); | printf("C FUNCTION, %jx\n", (uintmax_t)val->cfunc); | ||||
break; | break; | ||||
case L2_VAL_TYPE_ERROR: | |||||
printf("ERROR, %s\n", val->error); | |||||
break; | |||||
} | } | ||||
} | } | ||||
#include <string.h> | #include <string.h> | ||||
#include <stdio.h> | #include <stdio.h> | ||||
#include <stdarg.h> | |||||
#include "vm/builtins.h" | #include "vm/builtins.h" | ||||
free(val->buffer); | free(val->buffer); | ||||
} else if (typ == L2_VAL_TYPE_NAMESPACE) { | } else if (typ == L2_VAL_TYPE_NAMESPACE) { | ||||
free(val->ns); | free(val->ns); | ||||
} else if (typ == L2_VAL_TYPE_ERROR) { | |||||
free(val->error); | |||||
} | } | ||||
} | } | ||||
return id; | return id; | ||||
} | } | ||||
l2_word l2_vm_error(struct l2_vm *vm, const char *fmt, ...) { | |||||
l2_word id = alloc_val(vm); | |||||
struct l2_vm_value *val = &vm->values[id]; | |||||
val->flags = L2_VAL_CONST | L2_VAL_TYPE_ERROR; | |||||
char buf[256]; | |||||
va_list va; | |||||
va_start(va, fmt); | |||||
int n = vsnprintf(buf, sizeof(buf), fmt, va); | |||||
if (n < 0) { | |||||
const char *message = "Failed to generate error message!"; | |||||
val->error = malloc(strlen(message) + 1); | |||||
strcpy(val->error, message); | |||||
va_end(va); | |||||
return id; | |||||
} else if ((size_t)n + 1 < sizeof(buf)) { | |||||
val->error = malloc(n + 1); | |||||
strcpy(val->error, buf); | |||||
va_end(va); | |||||
return id; | |||||
} | |||||
val->error = malloc(n + 1); | |||||
vsnprintf(val->error, n + 1, fmt, va); | |||||
va_end(va); | |||||
return id; | |||||
} | |||||
void l2_vm_free(struct l2_vm *vm) { | void l2_vm_free(struct l2_vm *vm) { | ||||
// Skip ID 0, because that's always NONE | // Skip ID 0, because that's always NONE | ||||
for (size_t i = 1; i < vm->valuessize; ++i) { | for (size_t i = 1; i < vm->valuessize; ++i) { | ||||
// Don't interpret a non-function as a function | // Don't interpret a non-function as a function | ||||
if (typ != L2_VAL_TYPE_FUNCTION) { | if (typ != L2_VAL_TYPE_FUNCTION) { | ||||
// TODO: Error mechanism | |||||
vm->stack[vm->sptr++] = l2_vm_error(vm, "Attempt to call non-function"); | |||||
break; | break; | ||||
} | } | ||||