1095 lines
28 KiB
C
1095 lines
28 KiB
C
|
|
#ifndef WL_AMALGAMATION
|
|
#include "includes.h"
|
|
#include "value.h"
|
|
#include "eval.h"
|
|
#endif
|
|
|
|
#define FRAME_LIMIT 128
|
|
#define EVAL_STACK_LIMIT 128
|
|
#define GROUP_LIMIT 128
|
|
|
|
typedef struct {
|
|
int group;
|
|
int return_addr;
|
|
} Frame;
|
|
|
|
struct WL_State {
|
|
|
|
String code;
|
|
String data;
|
|
int off;
|
|
|
|
WL_Arena *a;
|
|
|
|
char *errbuf;
|
|
int errmax;
|
|
int errlen;
|
|
|
|
int num_frames;
|
|
Frame frames[FRAME_LIMIT];
|
|
|
|
int eval_depth;
|
|
Value eval_stack[EVAL_STACK_LIMIT];
|
|
|
|
int num_groups;
|
|
int groups[GROUP_LIMIT];
|
|
|
|
int cur_print;
|
|
int num_prints;
|
|
|
|
String sysvar;
|
|
String syscall;
|
|
bool syscall_error;
|
|
};
|
|
|
|
void eval_report(WL_State *state, char *fmt, ...)
|
|
{
|
|
if (state->errmax == 0 || state->errlen > 0)
|
|
return;
|
|
|
|
int len = snprintf(state->errbuf, state->errmax, "Error: ");
|
|
if (len < 0) {
|
|
// TODO
|
|
}
|
|
|
|
va_list args;
|
|
va_start(args, fmt);
|
|
int ret = vsnprintf(state->errbuf + len, state->errmax - len, fmt, args);
|
|
va_end(args);
|
|
if (ret < 0) {
|
|
// TODO
|
|
}
|
|
len += ret;
|
|
|
|
state->errlen = len;
|
|
}
|
|
|
|
static uint8_t read_u8(WL_State *state)
|
|
{
|
|
return state->code.ptr[state->off++];
|
|
}
|
|
|
|
static void read_mem(WL_State *state, void *dst, int len)
|
|
{
|
|
memcpy(dst, (uint8_t*) state->code.ptr + state->off, len);
|
|
state->off += len;
|
|
}
|
|
|
|
static uint32_t read_u32(WL_State *state)
|
|
{
|
|
uint32_t x;
|
|
read_mem(state, &x, (int) sizeof(x));
|
|
return x;
|
|
}
|
|
|
|
static int64_t read_s64(WL_State *state)
|
|
{
|
|
int64_t x;
|
|
read_mem(state, &x, (int) sizeof(x));
|
|
return x;
|
|
}
|
|
|
|
static double read_f64(WL_State *state)
|
|
{
|
|
double x;
|
|
read_mem(state, &x, (int) sizeof(x));
|
|
return x;
|
|
}
|
|
|
|
int step(WL_State *state)
|
|
{
|
|
uint8_t opcode = read_u8(state);
|
|
/*
|
|
printf("%-3d: ", e->off);
|
|
print_instruction(state->code.ptr + e->off, e->data.ptr);
|
|
printf("\n");
|
|
*/
|
|
|
|
switch (opcode) {
|
|
|
|
case OPCODE_NOPE:
|
|
{
|
|
// Do nothing
|
|
}
|
|
break;
|
|
|
|
case OPCODE_EXIT:
|
|
{
|
|
return 1;
|
|
}
|
|
break;
|
|
|
|
case OPCODE_GROUP:
|
|
{
|
|
state->groups[state->num_groups++] = state->eval_depth;
|
|
}
|
|
break;
|
|
|
|
case OPCODE_GPOP:
|
|
{
|
|
int group = state->groups[--state->num_groups];
|
|
state->eval_depth = group;
|
|
}
|
|
break;
|
|
|
|
case OPCODE_GPRINT:
|
|
{
|
|
state->num_prints = state->eval_depth - state->groups[state->num_groups-1];
|
|
}
|
|
break;
|
|
|
|
case OPCODE_GCOALESCE:
|
|
{
|
|
state->num_groups--;
|
|
}
|
|
break;
|
|
|
|
case OPCODE_GTRUNC:
|
|
{
|
|
uint32_t num = read_u32(state);
|
|
|
|
int group_size = state->eval_depth - state->groups[state->num_groups-1];
|
|
|
|
if (group_size < (int) num)
|
|
for (int i = 0; i < (int) num - group_size; i++)
|
|
state->eval_stack[state->eval_depth + i] = VALUE_NONE;
|
|
|
|
state->eval_depth = state->groups[state->num_groups-1] + num;
|
|
}
|
|
break;
|
|
|
|
case OPCODE_GOVERWRITE:
|
|
{
|
|
int current = state->groups[state->num_groups-1];
|
|
int parent = state->groups[state->num_groups-2];
|
|
|
|
int current_size = state->eval_depth - current;
|
|
|
|
for (int i = 0; i < current_size; i++)
|
|
state->eval_stack[parent + i] = state->eval_stack[current + i];
|
|
|
|
state->num_groups--;
|
|
state->eval_depth = parent + current_size;
|
|
}
|
|
break;
|
|
|
|
case OPCODE_GPACK:
|
|
{
|
|
Value array = make_array(state->a);
|
|
if (array == VALUE_ERROR) {
|
|
eval_report(state, "Out of memory");
|
|
return -1;
|
|
}
|
|
|
|
for (int i = state->groups[state->num_groups-1]; i < state->eval_depth; i++) {
|
|
int ret = array_append(state->a, array, state->eval_stack[i]);
|
|
if (ret < 0) {
|
|
eval_report(state, "Out of memory");
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
state->eval_depth = state->groups[--state->num_groups];
|
|
state->eval_stack[state->eval_depth++] = array;
|
|
}
|
|
break;
|
|
|
|
case OPCODE_PUSHN:
|
|
{
|
|
state->eval_stack[state->eval_depth++] = VALUE_NONE;
|
|
}
|
|
break;
|
|
|
|
case OPCODE_PUSHI:
|
|
{
|
|
int64_t x = read_s64(state);
|
|
|
|
Value v = make_int(state->a, x);
|
|
if (v == VALUE_ERROR) {
|
|
eval_report(state, "Out of memory");
|
|
return -1;
|
|
}
|
|
|
|
state->eval_stack[state->eval_depth++] = v;
|
|
}
|
|
break;
|
|
|
|
case OPCODE_PUSHF:
|
|
{
|
|
double x = read_f64(state);
|
|
|
|
Value v = make_float(state->a, x);
|
|
if (v == VALUE_ERROR) {
|
|
eval_report(state, "Out of memory");
|
|
return -1;
|
|
}
|
|
|
|
state->eval_stack[state->eval_depth++] = v;
|
|
}
|
|
break;
|
|
|
|
case OPCODE_PUSHS:
|
|
{
|
|
uint32_t off = read_u32(state);
|
|
uint32_t len = read_u32(state);
|
|
|
|
Value v = make_str(state->a, (String) { state->data.ptr + off, len });
|
|
if (v == VALUE_ERROR) {
|
|
eval_report(state, "Out of memory");
|
|
return -1;
|
|
}
|
|
|
|
state->eval_stack[state->eval_depth++] = v;
|
|
}
|
|
break;
|
|
|
|
case OPCODE_PUSHV:
|
|
{
|
|
uint8_t idx = read_u8(state);
|
|
|
|
int group = state->frames[state->num_frames-1].group;
|
|
Value v = state->eval_stack[state->groups[group] + idx];
|
|
|
|
state->eval_stack[state->eval_depth++] = v;
|
|
}
|
|
break;
|
|
|
|
case OPCODE_PUSHA:
|
|
{
|
|
uint32_t cap = read_u32(state);
|
|
(void) cap;
|
|
|
|
Value v = make_array(state->a);
|
|
if (v == VALUE_ERROR) {
|
|
eval_report(state, "Out of memory");
|
|
return -1;
|
|
}
|
|
|
|
state->eval_stack[state->eval_depth++] = v;
|
|
}
|
|
break;
|
|
|
|
case OPCODE_PUSHM:
|
|
{
|
|
uint32_t cap = read_u32(state);
|
|
(void) cap;
|
|
|
|
Value v = make_map(state->a);
|
|
if (v == VALUE_ERROR) {
|
|
eval_report(state, "Out of memory");
|
|
return -1;
|
|
}
|
|
|
|
state->eval_stack[state->eval_depth++] = v;
|
|
}
|
|
break;
|
|
|
|
case OPCODE_POP:
|
|
{
|
|
assert(state->num_groups == 0 || state->eval_depth > state->groups[state->num_groups-1]);
|
|
state->eval_depth--;
|
|
}
|
|
break;
|
|
|
|
case OPCODE_NEG:
|
|
{
|
|
Value a = state->eval_stack[--state->eval_depth];
|
|
Type t = type_of(a);
|
|
|
|
Value r;
|
|
if (0) {}
|
|
else if (t == TYPE_INT) r = make_int(state->a, -get_int(a));
|
|
else if (t == TYPE_FLOAT) r = make_float(state->a, -get_float(a));
|
|
else {
|
|
eval_report(state, "Invalid operation on non-numeric value");
|
|
return -1;
|
|
}
|
|
|
|
if (r == VALUE_ERROR) {
|
|
eval_report(state, "Out of memory");
|
|
return -1;
|
|
}
|
|
|
|
state->eval_stack[state->eval_depth++] = r;
|
|
}
|
|
break;
|
|
|
|
case OPCODE_EQL:
|
|
{
|
|
Value a = state->eval_stack[state->eval_depth-2];
|
|
Value b = state->eval_stack[state->eval_depth-1];
|
|
state->eval_depth -= 2;
|
|
|
|
Value r = valeq(a, b) ? VALUE_TRUE : VALUE_FALSE;
|
|
state->eval_stack[state->eval_depth++] = r;
|
|
}
|
|
break;
|
|
|
|
case OPCODE_NQL:
|
|
{
|
|
Value a = state->eval_stack[state->eval_depth-2];
|
|
Value b = state->eval_stack[state->eval_depth-1];
|
|
state->eval_depth -= 2;
|
|
|
|
Value r = valeq(a, b) ? VALUE_FALSE : VALUE_TRUE;
|
|
state->eval_stack[state->eval_depth++] = r;
|
|
}
|
|
break;
|
|
|
|
case OPCODE_LSS:
|
|
{
|
|
Value a = state->eval_stack[state->eval_depth-2];
|
|
Value b = state->eval_stack[state->eval_depth-1];
|
|
state->eval_depth -= 2;
|
|
|
|
if (type_of(a) != TYPE_INT || type_of(b) != TYPE_INT) {
|
|
eval_report(state, "Invalid operation on non-numeric value");
|
|
return -1;
|
|
}
|
|
|
|
Value r = valgrt(a, b) || valeq(a, b) ? VALUE_FALSE : VALUE_TRUE;
|
|
state->eval_stack[state->eval_depth++] = r;
|
|
}
|
|
break;
|
|
|
|
case OPCODE_GRT:
|
|
{
|
|
Value a = state->eval_stack[state->eval_depth-2];
|
|
Value b = state->eval_stack[state->eval_depth-1];
|
|
state->eval_depth -= 2;
|
|
|
|
if (type_of(a) != TYPE_INT || type_of(b) != TYPE_INT) {
|
|
eval_report(state, "Invalid operation on non-numeric value");
|
|
return -1;
|
|
}
|
|
|
|
Value r = valgrt(a, b) ? VALUE_TRUE : VALUE_FALSE;
|
|
state->eval_stack[state->eval_depth++] = r;
|
|
}
|
|
break;
|
|
|
|
case OPCODE_ADD:
|
|
{
|
|
Value a = state->eval_stack[state->eval_depth-2];
|
|
Value b = state->eval_stack[state->eval_depth-1];
|
|
state->eval_depth -= 2;
|
|
|
|
#define TYPE_PAIR(X, Y) (((uint16_t) (X) << 16) | (uint16_t) (Y))
|
|
|
|
Type t1 = type_of(a);
|
|
Type t2 = type_of(b);
|
|
|
|
Value r;
|
|
switch (TYPE_PAIR(t1, t2)) {
|
|
|
|
case TYPE_PAIR(TYPE_INT, TYPE_INT):
|
|
{
|
|
int64_t u = get_int(a);
|
|
int64_t v = get_int(b);
|
|
// TODO: check overflow and underflow
|
|
r = make_int(state->a, u + v);
|
|
}
|
|
break;
|
|
|
|
case TYPE_PAIR(TYPE_INT, TYPE_FLOAT):
|
|
{
|
|
float u = (float) get_int(a);
|
|
float v = get_float(b);
|
|
r = make_float(state->a, u + v);
|
|
}
|
|
break;
|
|
|
|
case TYPE_PAIR(TYPE_FLOAT, TYPE_INT):
|
|
{
|
|
float u = get_float(a);
|
|
float v = (float) get_int(b);
|
|
r = make_float(state->a, u + v);
|
|
}
|
|
break;
|
|
|
|
case TYPE_PAIR(TYPE_FLOAT, TYPE_FLOAT):
|
|
{
|
|
float u = get_float(a);
|
|
float v = get_float(b);
|
|
// TODO: check overflow and underflow
|
|
r = make_float(state->a, u + v);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
eval_report(state, "Invalid operation on non-numeric value");
|
|
return -1;
|
|
}
|
|
|
|
if (r == VALUE_ERROR) {
|
|
eval_report(state, "Out of memory");
|
|
return -1;
|
|
}
|
|
|
|
state->eval_stack[state->eval_depth++] = r;
|
|
}
|
|
break;
|
|
|
|
case OPCODE_SUB:
|
|
{
|
|
Value a = state->eval_stack[state->eval_depth-2];
|
|
Value b = state->eval_stack[state->eval_depth-1];
|
|
state->eval_depth -= 2;
|
|
|
|
Type t1 = type_of(a);
|
|
Type t2 = type_of(b);
|
|
|
|
Value r;
|
|
switch (TYPE_PAIR(t1, t2)) {
|
|
|
|
case TYPE_PAIR(TYPE_INT, TYPE_INT):
|
|
{
|
|
int64_t u = get_int(a);
|
|
int64_t v = get_int(b);
|
|
// TODO: check overflow and underflow
|
|
r = make_int(state->a, u - v);
|
|
}
|
|
break;
|
|
|
|
case TYPE_PAIR(TYPE_INT, TYPE_FLOAT):
|
|
{
|
|
float u = (float) get_int(a);
|
|
float v = get_float(b);
|
|
r = make_float(state->a, u - v);
|
|
}
|
|
break;
|
|
|
|
case TYPE_PAIR(TYPE_FLOAT, TYPE_INT):
|
|
{
|
|
float u = get_float(a);
|
|
float v = (float) get_int(b);
|
|
r = make_float(state->a, u - v);
|
|
}
|
|
break;
|
|
|
|
case TYPE_PAIR(TYPE_FLOAT, TYPE_FLOAT):
|
|
{
|
|
float u = get_float(a);
|
|
float v = get_float(b);
|
|
// TODO: check overflow and underflow
|
|
r = make_float(state->a, u - v);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
eval_report(state, "Invalid operation on non-numeric value");
|
|
return -1;
|
|
}
|
|
|
|
if (r == VALUE_ERROR) {
|
|
eval_report(state, "Out of memory");
|
|
return -1;
|
|
}
|
|
|
|
state->eval_stack[state->eval_depth++] = r;
|
|
}
|
|
break;
|
|
|
|
case OPCODE_MUL:
|
|
{
|
|
Value a = state->eval_stack[state->eval_depth-2];
|
|
Value b = state->eval_stack[state->eval_depth-1];
|
|
state->eval_depth -= 2;
|
|
|
|
Type t1 = type_of(a);
|
|
Type t2 = type_of(b);
|
|
|
|
Value r;
|
|
switch (TYPE_PAIR(t1, t2)) {
|
|
|
|
case TYPE_PAIR(TYPE_INT, TYPE_INT):
|
|
{
|
|
int64_t u = get_int(a);
|
|
int64_t v = get_int(b);
|
|
// TODO: check overflow and underflow
|
|
r = make_int(state->a, u * v);
|
|
}
|
|
break;
|
|
|
|
case TYPE_PAIR(TYPE_INT, TYPE_FLOAT):
|
|
{
|
|
float u = (float) get_int(a);
|
|
float v = get_float(b);
|
|
r = make_float(state->a, u * v);
|
|
}
|
|
break;
|
|
|
|
case TYPE_PAIR(TYPE_FLOAT, TYPE_INT):
|
|
{
|
|
float u = get_float(a);
|
|
float v = (float) get_int(b);
|
|
r = make_float(state->a, u * v);
|
|
}
|
|
break;
|
|
|
|
case TYPE_PAIR(TYPE_FLOAT, TYPE_FLOAT):
|
|
{
|
|
float u = get_float(a);
|
|
float v = get_float(b);
|
|
// TODO: check overflow and underflow
|
|
r = make_float(state->a, u * v);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
eval_report(state, "Invalid operation on non-numeric value");
|
|
return -1;
|
|
}
|
|
|
|
if (r == VALUE_ERROR) {
|
|
eval_report(state, "Out of memory");
|
|
return -1;
|
|
}
|
|
|
|
state->eval_stack[state->eval_depth++] = r;
|
|
}
|
|
break;
|
|
|
|
case OPCODE_DIV:
|
|
{
|
|
Value a = state->eval_stack[state->eval_depth-2];
|
|
Value b = state->eval_stack[state->eval_depth-1];
|
|
state->eval_depth -= 2;
|
|
|
|
Type t1 = type_of(a);
|
|
Type t2 = type_of(b);
|
|
|
|
Value r;
|
|
switch (TYPE_PAIR(t1, t2)) {
|
|
|
|
case TYPE_PAIR(TYPE_INT, TYPE_INT):
|
|
{
|
|
// TODO: check division by 0
|
|
|
|
int64_t u = get_int(a);
|
|
int64_t v = get_int(b);
|
|
r = make_int(state->a, u / v);
|
|
}
|
|
break;
|
|
|
|
case TYPE_PAIR(TYPE_INT, TYPE_FLOAT):
|
|
{
|
|
// TODO: check division by 0
|
|
|
|
float u = (float) get_int(a);
|
|
float v = get_float(b);
|
|
r = make_float(state->a, u / v);
|
|
}
|
|
break;
|
|
|
|
case TYPE_PAIR(TYPE_FLOAT, TYPE_INT):
|
|
{
|
|
// TODO: check division by 0
|
|
|
|
float u = get_float(a);
|
|
float v = (float) get_int(b);
|
|
r = make_float(state->a, u / v);
|
|
}
|
|
break;
|
|
|
|
case TYPE_PAIR(TYPE_FLOAT, TYPE_FLOAT):
|
|
{
|
|
float u = get_float(a);
|
|
float v = get_float(b);
|
|
r = make_float(state->a, u / v);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
eval_report(state, "Invalid operation on non-numeric value");
|
|
return -1;
|
|
}
|
|
|
|
if (r == VALUE_ERROR) {
|
|
eval_report(state, "Out of memory");
|
|
return -1;
|
|
}
|
|
|
|
state->eval_stack[state->eval_depth++] = r;
|
|
}
|
|
break;
|
|
|
|
case OPCODE_MOD:
|
|
{
|
|
Value a = state->eval_stack[state->eval_depth-2];
|
|
Value b = state->eval_stack[state->eval_depth-1];
|
|
state->eval_depth -= 2;
|
|
|
|
Type t1 = type_of(a);
|
|
Type t2 = type_of(b);
|
|
|
|
if (t1 != TYPE_INT || t2 != TYPE_INT) {
|
|
eval_report(state, "Invalid modulo operation on non-integer value");
|
|
return -1;
|
|
}
|
|
|
|
int64_t u = get_int(a);
|
|
int64_t v = get_int(b);
|
|
Value r = make_int(state->a, u % v);
|
|
if (r == VALUE_ERROR) {
|
|
eval_report(state, "Out of memory");
|
|
return -1;
|
|
}
|
|
|
|
state->eval_stack[state->eval_depth++] = r;
|
|
}
|
|
break;
|
|
|
|
case OPCODE_SETV:
|
|
{
|
|
uint8_t x = read_u8(state);
|
|
|
|
Frame *f = &state->frames[state->num_frames-1];
|
|
state->eval_stack[state->groups[f->group] + x] = state->eval_stack[--state->eval_depth];
|
|
}
|
|
break;
|
|
|
|
case OPCODE_JUMP:
|
|
{
|
|
uint32_t x = read_u32(state);
|
|
state->off = x;
|
|
}
|
|
break;
|
|
|
|
case OPCODE_JIFP:
|
|
{
|
|
uint32_t x = read_u32(state);
|
|
Value a = state->eval_stack[--state->eval_depth];
|
|
|
|
if (a == VALUE_FALSE)
|
|
state->off = x;
|
|
else {
|
|
if (a != VALUE_TRUE) {
|
|
eval_report(state, "Invalid operation on non-boolean value");
|
|
return -1;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case OPCODE_CALL:
|
|
{
|
|
uint32_t off = read_u32(state);
|
|
|
|
if (state->num_frames == FRAME_LIMIT) {
|
|
eval_report(state, "Frame limit reached");
|
|
return -1;
|
|
}
|
|
state->frames[state->num_frames++] = (Frame) {.return_addr=state->off, .group=state->num_groups-1};
|
|
|
|
state->off = off;
|
|
}
|
|
break;
|
|
|
|
case OPCODE_RET:
|
|
{
|
|
state->off = state->frames[--state->num_frames].return_addr;
|
|
}
|
|
break;
|
|
|
|
case OPCODE_APPEND:
|
|
{
|
|
Value val = state->eval_stack[state->eval_depth-1];
|
|
Value set = state->eval_stack[state->eval_depth-2];
|
|
state->eval_depth--;
|
|
|
|
if (type_of(set) != TYPE_ARRAY) {
|
|
eval_report(state, "Invalid operation on non-array value");
|
|
return -1;
|
|
}
|
|
|
|
int ret = array_append(state->a, set, val);
|
|
if (ret < 0) {
|
|
eval_report(state, "Out of memory");
|
|
return -1;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case OPCODE_INSERT1:
|
|
{
|
|
Value key = state->eval_stack[state->eval_depth-1];
|
|
Value val = state->eval_stack[state->eval_depth-2];
|
|
Value set = state->eval_stack[state->eval_depth-3];
|
|
state->eval_depth -= 2;
|
|
|
|
if (type_of(set) == TYPE_ARRAY) {
|
|
|
|
Value *dst = array_select(set, key);
|
|
if (dst == NULL) {
|
|
eval_report(state, "Index out of range");
|
|
return -1;
|
|
}
|
|
*dst = val;
|
|
|
|
} else if (type_of(set) == TYPE_MAP) {
|
|
|
|
int ret = map_insert(state->a, set, key, val);
|
|
if (ret < 0) {
|
|
eval_report(state, "Out of memory");
|
|
return -1;
|
|
}
|
|
|
|
} else {
|
|
eval_report(state, "Invalid insertion on non-array and non-map value");
|
|
return -1;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case OPCODE_INSERT2:
|
|
{
|
|
Value key = state->eval_stack[state->eval_depth-1];
|
|
Value set = state->eval_stack[state->eval_depth-2];
|
|
Value val = state->eval_stack[state->eval_depth-3];
|
|
state->eval_depth -= 2;
|
|
|
|
if (type_of(set) == TYPE_ARRAY) {
|
|
|
|
Value *dst = array_select(set, key);
|
|
if (dst == NULL) {
|
|
eval_report(state, "Index out of range");
|
|
return -1;
|
|
}
|
|
*dst = val;
|
|
|
|
} else if (type_of(set) == TYPE_MAP) {
|
|
|
|
int ret = map_insert(state->a, set, key, val);
|
|
if (ret < 0) {
|
|
eval_report(state, "Out of memory");
|
|
return -1;
|
|
}
|
|
|
|
} else {
|
|
eval_report(state, "Invalid insertion on non-array and non-map value");
|
|
return -1;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case OPCODE_SELECT:
|
|
{
|
|
Value key = state->eval_stack[state->eval_depth-1];
|
|
Value set = state->eval_stack[state->eval_depth-2];
|
|
state->eval_depth -= 2;
|
|
|
|
Value r;
|
|
if (type_of(set) == TYPE_ARRAY) {
|
|
|
|
Value *src = array_select(set, key);
|
|
if (src == NULL) {
|
|
assert(0); // TODO
|
|
}
|
|
r = *src;
|
|
|
|
} else if (type_of(set) == TYPE_MAP) {
|
|
|
|
int ret = map_select(set, key, &r);
|
|
if (ret < 0) {
|
|
assert(0); // TODO
|
|
}
|
|
|
|
} else {
|
|
eval_report(state, "Invalid selection from non-array and non-map value");
|
|
return -1;
|
|
}
|
|
|
|
state->eval_stack[state->eval_depth++] = r;
|
|
}
|
|
break;
|
|
|
|
case OPCODE_PRINT:
|
|
{
|
|
state->num_prints = 1;
|
|
}
|
|
break;
|
|
|
|
case OPCODE_SYSVAR:
|
|
{
|
|
uint32_t off = read_u32(state);
|
|
uint32_t len = read_u32(state);
|
|
String name = { state->data.ptr + off, len };
|
|
|
|
state->sysvar = name;
|
|
}
|
|
break;
|
|
|
|
case OPCODE_SYSCALL:
|
|
{
|
|
uint32_t off = read_u32(state);
|
|
uint32_t len = read_u32(state);
|
|
String name = { state->data.ptr + off, len };
|
|
|
|
state->syscall = name;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
eval_report(state, "Invalid opcode (offset %d)", state->off-1);
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
WL_State *WL_State_init(WL_Arena *a, WL_Program p, char *err, int errmax)
|
|
{
|
|
WL_State *state = alloc(a, (int) sizeof(WL_State), _Alignof(WL_State));
|
|
if (state == NULL)
|
|
return NULL;
|
|
|
|
String code;
|
|
String data;
|
|
|
|
int ret = parse_program_header(p, &code, &data, err, errmax);
|
|
if (ret < 0)
|
|
return NULL;
|
|
|
|
*state = (WL_State) {
|
|
.code=code,
|
|
.data=data,
|
|
.off=0,
|
|
.a=a,
|
|
.errbuf=err,
|
|
.errmax=errmax,
|
|
.errlen=0,
|
|
.num_frames=0,
|
|
.eval_depth=0,
|
|
.num_groups=0,
|
|
.num_prints=0,
|
|
.cur_print=0,
|
|
};
|
|
|
|
state->frames[state->num_frames++] = (Frame) { 0, 0 };
|
|
|
|
return state;
|
|
}
|
|
|
|
void WL_State_free(WL_State *state)
|
|
{
|
|
state->num_frames--;
|
|
|
|
// TODO
|
|
}
|
|
|
|
WL_Result WL_eval(WL_State *state)
|
|
{
|
|
if (state->sysvar.len > 0) {
|
|
|
|
if (state->syscall_error)
|
|
return (WL_Result) { WL_ERROR, (WL_String) { NULL, 0 } };
|
|
|
|
// TODO
|
|
state->sysvar = S("");
|
|
}
|
|
|
|
if (state->syscall.len > 0) {
|
|
|
|
if (state->syscall_error)
|
|
return (WL_Result) { WL_ERROR, (WL_String) { NULL, 0 } };
|
|
|
|
state->syscall = S("");
|
|
}
|
|
|
|
while (state->num_prints == 0) {
|
|
|
|
int ret = step(state);
|
|
if (ret < 0) return (WL_Result) { WL_ERROR, (WL_String) { NULL, 0 } };
|
|
if (ret == 1) return (WL_Result) { WL_DONE, (WL_String) { NULL, 0 } };
|
|
|
|
if (state->sysvar.len > 0)
|
|
return (WL_Result) { WL_VAR, (WL_String) { state->sysvar.ptr, state->sysvar.len } };
|
|
|
|
if (state->syscall.len > 0)
|
|
return (WL_Result) { WL_CALL, (WL_String) { state->syscall.ptr, state->syscall.len } };
|
|
}
|
|
|
|
Value v = state->eval_stack[state->eval_depth - state->num_prints + state->cur_print];
|
|
|
|
state->cur_print++;
|
|
if (state->cur_print == state->num_prints) {
|
|
state->cur_print = 0;
|
|
state->num_prints = 0;
|
|
}
|
|
|
|
WL_String str;
|
|
|
|
if (type_of(v) == TYPE_STRING) {
|
|
String str2 = get_str(v);
|
|
str.ptr = str2.ptr;
|
|
str.len = str2.len;
|
|
} else {
|
|
int cap = 8;
|
|
char *dst = alloc(state->a, cap, 1);
|
|
int len = value_to_string(v, dst, cap);
|
|
if (len > cap) {
|
|
if (!grow_alloc(state->a, dst, len)) {
|
|
assert(0); // TODO
|
|
}
|
|
value_to_string(v, dst, len);
|
|
}
|
|
str.ptr = dst;
|
|
str.len = len;
|
|
}
|
|
|
|
return (WL_Result) { WL_OUTPUT, str };
|
|
}
|
|
|
|
static bool in_syscall(WL_State *state)
|
|
{
|
|
return (state->syscall.len > 0 || state->sysvar.len > 0) && !state->syscall_error;
|
|
}
|
|
|
|
int WL_popint(WL_State *state, long long *x)
|
|
{
|
|
if (!in_syscall(state)) return 0;
|
|
|
|
if (state->eval_depth == 0)
|
|
return 0;
|
|
|
|
Value v = state->eval_stack[state->eval_depth-1];
|
|
if (type_of(v) != TYPE_INT)
|
|
return 0;
|
|
|
|
*x = get_int(v);
|
|
|
|
state->eval_depth--;
|
|
return true;
|
|
}
|
|
|
|
int WL_popfloat(WL_State *state, float *x)
|
|
{
|
|
if (!in_syscall(state)) return 0;
|
|
|
|
if (state->eval_depth == 0)
|
|
return 0;
|
|
|
|
Value v = state->eval_stack[state->eval_depth-1];
|
|
if (type_of(v) != TYPE_FLOAT)
|
|
return 0;
|
|
|
|
*x = get_float(v);
|
|
|
|
state->eval_depth--;
|
|
return true;
|
|
}
|
|
|
|
int WL_popstr(WL_State *state, char **str, int *len)
|
|
{
|
|
if (!in_syscall(state)) return 0;
|
|
|
|
if (state->eval_depth == 0)
|
|
return 0;
|
|
|
|
Value v = state->eval_stack[state->eval_depth-1];
|
|
if (type_of(v) != TYPE_STRING)
|
|
return 0;
|
|
|
|
String s = get_str(v);
|
|
*str = s.ptr;
|
|
*len = s.len;
|
|
|
|
state->eval_depth--;
|
|
return true;
|
|
}
|
|
|
|
int WL_popany(WL_State *state)
|
|
{
|
|
if (!in_syscall(state))
|
|
return 0;
|
|
|
|
if (state->eval_depth == 0)
|
|
return 0;
|
|
|
|
state->eval_depth--;
|
|
return 1;
|
|
}
|
|
|
|
void WL_select(WL_State *state)
|
|
{
|
|
// TODO
|
|
}
|
|
|
|
void WL_pushint(WL_State *state, long long x)
|
|
{
|
|
if (!in_syscall(state)) return;
|
|
|
|
Value v = make_int(state->a, x);
|
|
if (v == VALUE_ERROR) {
|
|
eval_report(state, "Out of memory");
|
|
state->syscall_error = true;
|
|
return;
|
|
}
|
|
|
|
state->eval_stack[state->eval_depth++] = v;
|
|
}
|
|
|
|
void WL_pushfloat(WL_State *state, float x)
|
|
{
|
|
if (!in_syscall(state)) return;
|
|
|
|
Value v = make_float(state->a, x);
|
|
if (v == VALUE_ERROR) {
|
|
eval_report(state, "Out of memory");
|
|
state->syscall_error = true;
|
|
return;
|
|
}
|
|
|
|
state->eval_stack[state->eval_depth++] = v;
|
|
}
|
|
|
|
void WL_pushstr(WL_State *state, char *str, int len)
|
|
{
|
|
if (!in_syscall(state)) return;
|
|
|
|
Value v = make_str(state->a, (String) { str, len });
|
|
if (v == VALUE_ERROR) {
|
|
eval_report(state, "Out of memory");
|
|
state->syscall_error = true;
|
|
return;
|
|
}
|
|
|
|
state->eval_stack[state->eval_depth++] = v;
|
|
}
|
|
|
|
void WL_pusharray(WL_State *state, int cap)
|
|
{
|
|
if (!in_syscall(state)) return;
|
|
|
|
(void) cap;
|
|
Value v = make_array(state->a);
|
|
if (v == VALUE_ERROR) {
|
|
eval_report(state, "Out of memory");
|
|
state->syscall_error = true;
|
|
return;
|
|
}
|
|
|
|
state->eval_stack[state->eval_depth++] = v;
|
|
}
|
|
|
|
void WL_pushmap(WL_State *state, int cap)
|
|
{
|
|
if (!in_syscall(state)) return;
|
|
|
|
(void) cap;
|
|
Value v = make_map(state->a);
|
|
if (v == VALUE_ERROR) {
|
|
eval_report(state, "Out of memory");
|
|
state->syscall_error = true;
|
|
return;
|
|
}
|
|
|
|
state->eval_stack[state->eval_depth++] = v;
|
|
}
|
|
|
|
void WL_insert(WL_State *state)
|
|
{
|
|
// TODO
|
|
} |