Files
WL/src/eval.c
T
2025-08-04 15:36:59 +02:00

1274 lines
30 KiB
C

#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include <assert.h>
#ifndef WL_AMALGAMATION
#include "eval.h"
#endif
#define FRAME_LIMIT 128
#define EVAL_STACK_LIMIT 128
#define GROUP_LIMIT 128
#define HEAP_BASE 0xFEEDBEEFFEEDBEEF
/*
int
float
array
map
html
bool
none
*/
typedef enum {
TYPE_NONE,
TYPE_BOOL,
TYPE_INT,
TYPE_FLOAT,
TYPE_MAP,
TYPE_ARRAY,
TYPE_STRING,
TYPE_ERROR,
} Type;
typedef uint64_t Value;
#define ITEMS_PER_MAP_BATCH 8
#define ITEMS_PER_ARRAY_BATCH 16
typedef struct MapItems MapItems;
struct MapItems {
MapItems *next;
Value keys [ITEMS_PER_MAP_BATCH];
Value items[ITEMS_PER_MAP_BATCH];
};
typedef struct {
Type type;
int count;
int tail_count;
MapItems head;
MapItems *tail;
} MapValue;
typedef struct ArrayItems ArrayItems;
struct ArrayItems {
ArrayItems *next;
Value items[ITEMS_PER_ARRAY_BATCH];
};
typedef struct {
Type type;
int count;
int tail_count;
ArrayItems head;
ArrayItems *tail;
} ArrayValue;
typedef struct {
Type type;
double raw;
} FloatValue;
typedef struct {
Type type;
int64_t raw;
} IntValue;
typedef struct {
Type type;
int len;
char data[];
} StringValue;
typedef struct {
int group;
int return_addr;
} Frame;
typedef struct {
String code;
String data;
int off;
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];
} Eval;
#define VALUE_NONE ((Value) 0)
#define VALUE_TRUE ((Value) 1)
#define VALUE_FALSE ((Value) 2)
#define VALUE_ERROR ((Value) 6)
void eval_report(Eval *e, char *fmt, ...)
{
if (e->errmax == 0 || e->errlen > 0)
return;
int len = snprintf(e->errbuf, e->errmax, "Error: ");
if (len < 0) {
// TODO
}
va_list args;
va_start(args, fmt);
int ret = vsnprintf(e->errbuf + len, e->errmax - len, fmt, args);
va_end(args);
if (ret < 0) {
// TODO
}
len += ret;
e->errlen = len;
}
Type type_of(Value v)
{
// 000 none
// 001 true
// 010 false
// 011 int
// 100
// 101
// 110 error
// 111 pointer
switch (v & 7) {
case 0: return TYPE_NONE;
case 1: return TYPE_BOOL;
case 2: return TYPE_BOOL;
case 3: return TYPE_INT;
case 4: break;
case 5: break;
case 6: return TYPE_ERROR;
case 7: return *(Type*) ((uintptr_t) v & ~(uintptr_t) 7);
}
return TYPE_ERROR;
}
int64_t get_int(Value v)
{
if ((v & 7) == 3)
return (int64_t) (v >> 3);
IntValue *p = (IntValue*) v;
return p->raw;
}
float get_float(Value v)
{
FloatValue *p = (FloatValue*) v;
return p->raw;
}
String get_str(Value v)
{
StringValue *p = (StringValue*) (v & ~(uintptr_t) 7);
return (String) { p->data, p->len };
}
MapValue *get_map(Value v)
{
return (MapValue*) (v & ~(uintptr_t) 7);
}
ArrayValue *get_array(Value v)
{
return (ArrayValue*) (v & ~(uintptr_t) 7);
}
Value make_int(Eval *e, int64_t x)
{
if (x <= (int64_t) (1ULL << 60)-1 && x >= (int64_t) -(1ULL << 60))
return ((Value) x << 3) | 3;
IntValue *v = alloc(e->a, (int) sizeof(IntValue), _Alignof(IntValue));
if (v == NULL) {
eval_report(e, "Out of memory");
return VALUE_ERROR;
}
v->type = TYPE_INT;
v->raw = x;
assert(((uintptr_t) v & 7) == 0);
return ((Value) v) | 7;
}
Value make_float(Eval *e, float x)
{
FloatValue *v = alloc(e->a, (int) sizeof(FloatValue), _Alignof(FloatValue));
if (v == NULL) {
eval_report(e, "Out of memory");
return VALUE_ERROR;
}
v->type = TYPE_FLOAT;
v->raw = x;
assert(((uintptr_t) v & 7) == 0);
return ((Value) v) | 7;
}
Value make_str(Eval *e, String x) // TODO: This should reuse the string contents when possible
{
StringValue *v = alloc(e->a, (int) sizeof(StringValue) + x.len, 8);
if (v == NULL) {
eval_report(e, "Out of memory");
return VALUE_ERROR;
}
v->type = TYPE_STRING;
v->len = x.len;
memcpy(v->data, x.ptr, x.len);
assert(((uintptr_t) v & 7) == 0);
return ((Value) v) | 7;
}
Value make_map(Eval *e)
{
MapValue *m = alloc(e->a, (int) sizeof(MapValue), _Alignof(MapValue));
if (m == NULL) {
eval_report(e, "Out of memory");
return VALUE_ERROR;
}
m->type = TYPE_MAP;
m->count = 0;
m->tail_count = 0;
m->tail = &m->head;
return (Value) m | 7;
}
Value make_array(Eval *e)
{
ArrayValue *a = alloc(e->a, (int) sizeof(ArrayValue), _Alignof(ArrayValue));
if (a == NULL) {
eval_report(e, "Out of memory");
return VALUE_ERROR;
}
a->type = TYPE_ARRAY;
a->count = 0;
a->tail_count = 0;
a->tail = &a->head;
return (Value) a | 7;
}
b32 valeq(Value a, Value b);
int map_select(Eval *e, Value map, Value key, Value *val)
{
(void) e;
MapValue *p = get_map(map);
MapItems *batch = &p->head;
while (batch) {
int num = ITEMS_PER_MAP_BATCH;
if (batch->next == NULL)
num = p->tail_count;
for (int i = 0; i < num; i++)
if (valeq(batch->keys[i], key)) {
*val = batch->items[i];
return 0;
}
batch = batch->next;
}
return -1;
}
int map_insert(Eval *e, Value map, Value key, Value val)
{
MapValue *p = get_map(map);
if (p->tail_count == ITEMS_PER_MAP_BATCH) {
MapItems *batch = alloc(e->a, (int) sizeof(MapItems), _Alignof(MapItems));
if (batch == NULL) {
eval_report(e, "Out of memory");
return -1;
}
batch->next = NULL;
p->tail = batch;
p->tail_count = 0;
}
p->tail->keys[p->tail_count] = key;
p->tail->items[p->tail_count] = val;
p->tail_count++;
p->count++;
return 0;
}
void value_print(Value v);
void map_print(Value v)
{
printf("{ ");
MapValue *p = get_map(v);
MapItems *batch = &p->head;
while (batch) {
int num = ITEMS_PER_MAP_BATCH;
if (batch->next == NULL)
num = p->tail_count;
for (int i = 0; i < num; i++) {
value_print(batch->keys[i]);
printf(": ");
value_print(batch->items[i]);
printf(", ");
}
batch = batch->next;
}
printf("}");
}
Value *array_select(Eval *e, Value array, int key)
{
(void) e;
ArrayValue *p = get_array(array);
ArrayItems *batch = &p->head;
int cursor = 0;
while (batch) {
int num = ITEMS_PER_MAP_BATCH;
if (batch->next == NULL)
num = p->tail_count;
if (cursor <= key && key < cursor + num)
return &batch->items[key - cursor];
batch = batch->next;
cursor += num;
}
return NULL;
}
int array_append(Eval *e, Value array, Value val)
{
ArrayValue *p = get_array(array);
if (p->tail_count == ITEMS_PER_MAP_BATCH) {
ArrayItems *batch = alloc(e->a, (int) sizeof(ArrayItems), _Alignof(ArrayItems));
if (batch == NULL) {
eval_report(e, "Out of memory");
return -1;
}
batch->next = NULL;
p->tail = batch;
p->tail_count = 0;
}
p->tail->items[p->tail_count] = val;
p->tail_count++;
p->count++;
return 0;
}
void array_print(Value v)
{
ArrayValue *p = get_array(v);
ArrayItems *batch = &p->head;
int cursor = 0;
while (batch) {
int num = ITEMS_PER_MAP_BATCH;
if (batch->next == NULL)
num = p->tail_count;
for (int i = 0; i < num; i++)
value_print(batch->items[i]);
batch = batch->next;
cursor += num;
}
}
b32 valeq(Value a, Value b)
{
Type t1 = type_of(a);
Type t2 = type_of(b);
if (t1 != t2)
return false;
switch (t1) {
case TYPE_NONE:
return VALUE_TRUE;
case TYPE_BOOL:
return a == b;
case TYPE_INT:
return get_int(a) == get_int(b);
case TYPE_FLOAT:
return get_float(a) == get_float(b);
case TYPE_MAP:
return false; // TODO
case TYPE_ARRAY:
return false; // TODO
case TYPE_STRING:
return streq(get_str(a), get_str(b));
case TYPE_ERROR:
return true;
}
return false;
}
b32 valgrt(Value a, Value b)
{
Type t1 = type_of(a);
Type t2 = type_of(b);
if (t1 != t2)
return false;
switch (t1) {
case TYPE_NONE:
return VALUE_FALSE;
case TYPE_BOOL:
return VALUE_FALSE;
case TYPE_INT:
return get_int(a) > get_int(b);
case TYPE_FLOAT:
return get_float(a) > get_float(b);
case TYPE_MAP:
return false;
case TYPE_ARRAY:
return false;
case TYPE_STRING:
return false;
case TYPE_ERROR:
return false;
}
return false;
}
void value_print(Value v)
{
switch (type_of(v)) {
case TYPE_NONE:
printf("none");
break;
case TYPE_BOOL:
printf(v == VALUE_TRUE ? "true" : "false");
break;
case TYPE_INT:
printf("%" LLD, get_int(v));
break;
case TYPE_FLOAT:
printf("%lf", get_float(v));
break;
case TYPE_MAP:
map_print(v);
break;
case TYPE_ARRAY:
array_print(v);
break;
case TYPE_STRING:
{
String s = get_str(v);
printf("%.*s", s.len, s.ptr);
}
break;
case TYPE_ERROR:
printf("error");
break;
}
fflush(stdout);
}
int step(Eval *e)
{
uint8_t opcode = e->code.ptr[e->off];
/*
printf("%-3d: ", e->off);
print_instruction(e->code.ptr + e->off, e->data.ptr);
printf("\n");
*/
e->off++;
switch (opcode) {
case OPCODE_NOPE:
{
// Do nothing
}
break;
case OPCODE_EXIT:
{
return 1;
}
break;
case OPCODE_GROUP:
{
e->groups[e->num_groups++] = e->eval_depth;
}
break;
case OPCODE_GPOP:
{
int group = e->groups[--e->num_groups];
e->eval_depth = group;
}
break;
case OPCODE_GPRINT:
{
for (int i = e->groups[e->num_groups-1]; i < e->eval_depth; i++)
value_print(e->eval_stack[i]);
}
break;
case OPCODE_GCOALESCE:
{
e->num_groups--;
}
break;
case OPCODE_GTRUNC:
{
uint32_t num;
memcpy(&num, (uint8_t*) e->code.ptr + e->off, sizeof(uint32_t));
e->off += (int) sizeof(uint32_t);
int group_size = e->eval_depth - e->groups[e->num_groups-1];
if (group_size < (int) num) {
for (int i = 0; i < (int) num - group_size; i++)
e->eval_stack[e->eval_depth + i] = VALUE_NONE;
}
e->eval_depth = e->groups[e->num_groups-1] + num;
}
break;
case OPCODE_GOVERWRITE:
{
int current = e->groups[e->num_groups-1];
int parent = e->groups[e->num_groups-2];
int current_size = e->eval_depth - current;
for (int i = 0; i < current_size; i++)
e->eval_stack[parent + i] = e->eval_stack[current + i];
e->num_groups--;
e->eval_depth = parent + current_size;
}
break;
case OPCODE_GPACK:
{
Value array = make_array(e);
if (array == VALUE_ERROR)
return -1;
for (int i = e->groups[e->num_groups-1]; i < e->eval_depth; i++)
array_append(e, array, e->eval_stack[i]);
e->eval_depth = e->groups[--e->num_groups];
e->eval_stack[e->eval_depth++] = array;
}
break;
case OPCODE_PUSHN:
{
e->eval_stack[e->eval_depth++] = VALUE_NONE;
}
break;
case OPCODE_PUSHI:
{
int64_t x;
memcpy(&x, (uint8_t*) e->code.ptr + e->off, sizeof(x));
e->off += (int) sizeof(x);
Value v = make_int(e, x);
if (v == VALUE_ERROR) return -1;
e->eval_stack[e->eval_depth++] = v;
}
break;
case OPCODE_PUSHF:
{
double x;
memcpy(&x, (uint8_t*) e->code.ptr + e->off, sizeof(x));
e->off += (int) sizeof(x);
Value v = make_float(e, x);
if (v == VALUE_ERROR) return -1;
e->eval_stack[e->eval_depth++] = v;
}
break;
case OPCODE_PUSHS:
{
uint32_t off;
memcpy(&off, (uint8_t*) e->code.ptr + e->off, sizeof(uint32_t));
e->off += (int) sizeof(uint32_t);
uint32_t len;
memcpy(&len, (uint8_t*) e->code.ptr + e->off, sizeof(uint32_t));
e->off += (int) sizeof(uint32_t);
Value v = make_str(e, (String) { e->data.ptr + off, len });
if (v == VALUE_ERROR) return -1;
e->eval_stack[e->eval_depth++] = v;
}
break;
case OPCODE_PUSHV:
{
uint8_t idx;
memcpy(&idx, (uint8_t*) e->code.ptr + e->off, sizeof(uint8_t));
e->off += sizeof(uint8_t);
int group = e->frames[e->num_frames-1].group;
Value v = e->eval_stack[e->groups[group] + idx];
e->eval_stack[e->eval_depth++] = v;
}
break;
case OPCODE_PUSHA:
{
uint32_t cap;
memcpy(&cap, (uint8_t*) e->code.ptr + e->off, sizeof(uint32_t));
e->off += sizeof(uint32_t);
Value v = make_array(e);
if (v == VALUE_ERROR) return -1;
e->eval_stack[e->eval_depth++] = v;
}
break;
case OPCODE_PUSHM:
{
uint32_t cap;
memcpy(&cap, (uint8_t*) e->code.ptr + e->off, sizeof(uint32_t));
e->off += sizeof(uint32_t);
Value v = make_map(e);
if (v == VALUE_ERROR) return -1;
e->eval_stack[e->eval_depth++] = v;
}
break;
case OPCODE_POP:
{
assert(e->num_groups == 0 || e->eval_depth > e->groups[e->num_groups-1]);
e->eval_depth--;
}
break;
case OPCODE_NEG:
{
Value a = e->eval_stack[--e->eval_depth];
Type t = type_of(a);
Value r;
if (0) {}
else if (t == TYPE_INT) r = make_int(e, -get_int(a));
else if (t == TYPE_FLOAT) r = make_float(e, -get_float(a));
else {
eval_report(e, "Invalid operation on non-numeric value");
return -1;
}
e->eval_stack[e->eval_depth++] = r;
}
break;
case OPCODE_EQL:
{
Value a = e->eval_stack[e->eval_depth-2];
Value b = e->eval_stack[e->eval_depth-1];
e->eval_depth -= 2;
Value r = valeq(a, b) ? VALUE_TRUE : VALUE_FALSE;
e->eval_stack[e->eval_depth++] = r;
}
break;
case OPCODE_NQL:
{
Value a = e->eval_stack[e->eval_depth-2];
Value b = e->eval_stack[e->eval_depth-1];
e->eval_depth -= 2;
Value r = valeq(a, b) ? VALUE_FALSE : VALUE_TRUE;
e->eval_stack[e->eval_depth++] = r;
}
break;
case OPCODE_LSS:
{
Value a = e->eval_stack[e->eval_depth-2];
Value b = e->eval_stack[e->eval_depth-1];
e->eval_depth -= 2;
if (type_of(a) != TYPE_INT || type_of(b) != TYPE_INT) {
eval_report(e, "Invalid operation on non-numeric value");
return -1;
}
Value r = valgrt(a, b) || valeq(a, b) ? VALUE_FALSE : VALUE_TRUE;
e->eval_stack[e->eval_depth++] = r;
}
break;
case OPCODE_GRT:
{
Value a = e->eval_stack[e->eval_depth-2];
Value b = e->eval_stack[e->eval_depth-1];
e->eval_depth -= 2;
if (type_of(a) != TYPE_INT || type_of(b) != TYPE_INT) {
eval_report(e, "Invalid operation on non-numeric value");
return -1;
}
Value r = valgrt(a, b) ? VALUE_TRUE : VALUE_FALSE;
e->eval_stack[e->eval_depth++] = r;
}
break;
case OPCODE_ADD:
{
Value a = e->eval_stack[e->eval_depth-2];
Value b = e->eval_stack[e->eval_depth-1];
e->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(e, u + v);
if (r == VALUE_ERROR) return -1;
}
break;
case TYPE_PAIR(TYPE_INT, TYPE_FLOAT):
{
float u = (float) get_int(a);
float v = get_float(b);
r = make_float(e, u + v);
if (r == VALUE_ERROR) return -1;
}
break;
case TYPE_PAIR(TYPE_FLOAT, TYPE_INT):
{
float u = get_float(a);
float v = (float) get_int(b);
r = make_float(e, u + v);
if (r == VALUE_ERROR) return -1;
}
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(e, u + v);
if (r == VALUE_ERROR) return -1;
}
break;
default:
eval_report(e, "Invalid operation on non-numeric value");
return -1;
}
e->eval_stack[e->eval_depth++] = r;
}
break;
case OPCODE_SUB:
{
Value a = e->eval_stack[e->eval_depth-2];
Value b = e->eval_stack[e->eval_depth-1];
e->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(e, u - v);
if (r == VALUE_ERROR) return -1;
}
break;
case TYPE_PAIR(TYPE_INT, TYPE_FLOAT):
{
float u = (float) get_int(a);
float v = get_float(b);
r = make_float(e, u - v);
if (r == VALUE_ERROR) return -1;
}
break;
case TYPE_PAIR(TYPE_FLOAT, TYPE_INT):
{
float u = get_float(a);
float v = (float) get_int(b);
r = make_float(e, u - v);
if (r == VALUE_ERROR) return -1;
}
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(e, u - v);
if (r == VALUE_ERROR) return -1;
}
break;
default:
eval_report(e, "Invalid operation on non-numeric value");
return -1;
}
e->eval_stack[e->eval_depth++] = r;
}
break;
case OPCODE_MUL:
{
Value a = e->eval_stack[e->eval_depth-2];
Value b = e->eval_stack[e->eval_depth-1];
e->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(e, u * v);
if (r == VALUE_ERROR) return -1;
}
break;
case TYPE_PAIR(TYPE_INT, TYPE_FLOAT):
{
float u = (float) get_int(a);
float v = get_float(b);
r = make_float(e, u * v);
if (r == VALUE_ERROR) return -1;
}
break;
case TYPE_PAIR(TYPE_FLOAT, TYPE_INT):
{
float u = get_float(a);
float v = (float) get_int(b);
r = make_float(e, u * v);
if (r == VALUE_ERROR) return -1;
}
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(e, u * v);
if (r == VALUE_ERROR) return -1;
}
break;
default:
eval_report(e, "Invalid operation on non-numeric value");
return -1;
}
e->eval_stack[e->eval_depth++] = r;
}
break;
case OPCODE_DIV:
{
Value a = e->eval_stack[e->eval_depth-2];
Value b = e->eval_stack[e->eval_depth-1];
e->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(e, u / v);
if (r == VALUE_ERROR) return -1;
}
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(e, u / v);
if (r == VALUE_ERROR) return -1;
}
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(e, u / v);
if (r == VALUE_ERROR) return -1;
}
break;
case TYPE_PAIR(TYPE_FLOAT, TYPE_FLOAT):
{
float u = get_float(a);
float v = get_float(b);
r = make_float(e, u / v);
if (r == VALUE_ERROR) return -1;
}
break;
default:
eval_report(e, "Invalid operation on non-numeric value");
return -1;
}
e->eval_stack[e->eval_depth++] = r;
}
break;
case OPCODE_MOD:
{
Value a = e->eval_stack[e->eval_depth-2];
Value b = e->eval_stack[e->eval_depth-1];
e->eval_depth -= 2;
Type t1 = type_of(a);
Type t2 = type_of(b);
if (t1 != TYPE_INT || t2 != TYPE_INT) {
eval_report(e, "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(e, u % v);
if (r == VALUE_ERROR) return -1;
e->eval_stack[e->eval_depth++] = r;
}
break;
case OPCODE_SETV:
{
uint8_t x;
memcpy(&x, (uint8_t*) e->code.ptr + e->off, (int) sizeof(x));
e->off += (int) sizeof(x);
Frame *f = &e->frames[e->num_frames-1];
e->eval_stack[e->groups[f->group] + x] = e->eval_stack[--e->eval_depth];
}
break;
case OPCODE_JUMP:
{
uint32_t x;
memcpy(&x, (uint8_t*) e->code.ptr + e->off, (int) sizeof(x));
e->off = x;
}
break;
case OPCODE_JIFP:
{
uint32_t x;
memcpy(&x, (uint8_t*) e->code.ptr + e->off, (int) sizeof(x));
e->off += (int) sizeof(x);
Value a = e->eval_stack[--e->eval_depth];
if (a == VALUE_FALSE)
e->off = x;
else {
if (a != VALUE_TRUE) {
eval_report(e, "Invalid operation on non-boolean value");
return -1;
}
}
}
break;
case OPCODE_CALL:
{
uint32_t off;
memcpy(&off, (uint8_t*) e->code.ptr + e->off, sizeof(uint32_t));
e->off += (int) sizeof(uint32_t);
if (e->num_frames == FRAME_LIMIT) {
eval_report(e, "Frame limit reached");
return -1;
}
e->frames[e->num_frames++] = (Frame) {.return_addr=e->off, .group=e->num_groups-1};
e->off = off;
}
break;
case OPCODE_RET:
{
e->off = e->frames[--e->num_frames].return_addr;
}
break;
case OPCODE_APPEND:
{
Value val = e->eval_stack[e->eval_depth-1];
Value set = e->eval_stack[e->eval_depth-2];
e->eval_depth--;
if (type_of(set) != TYPE_ARRAY) {
eval_report(e, "Invalid operation on non-array value");
return -1;
}
int ret = array_append(e, set, val);
if (ret < 0) return -1;
}
break;
case OPCODE_INSERT1:
{
Value key = e->eval_stack[e->eval_depth-1];
Value val = e->eval_stack[e->eval_depth-2];
Value set = e->eval_stack[e->eval_depth-3];
e->eval_depth -= 2;
if (type_of(set) == TYPE_ARRAY) {
Value *dst = array_select(e, set, key);
if (dst == NULL) {
eval_report(e, "Index out of range");
return -1;
}
*dst = val;
} else if (type_of(set) == TYPE_MAP) {
int ret = map_insert(e, set, key, val);
if (ret < 0) return -1;
} else {
eval_report(e, "Invalid insertion on non-array and non-map value");
return -1;
}
}
break;
case OPCODE_INSERT2:
{
Value key = e->eval_stack[e->eval_depth-1];
Value set = e->eval_stack[e->eval_depth-2];
Value val = e->eval_stack[e->eval_depth-3];
e->eval_depth -= 2;
if (type_of(set) == TYPE_ARRAY) {
Value *dst = array_select(e, set, key);
if (dst == NULL) {
eval_report(e, "Index out of range");
return -1;
}
*dst = val;
} else if (type_of(set) == TYPE_MAP) {
int ret = map_insert(e, set, key, val);
if (ret < 0) return -1;
} else {
eval_report(e, "Invalid insertion on non-array and non-map value");
return -1;
}
}
break;
case OPCODE_SELECT:
{
Value key = e->eval_stack[e->eval_depth-1];
Value set = e->eval_stack[e->eval_depth-2];
e->eval_depth -= 2;
Value r;
if (type_of(set) == TYPE_ARRAY) {
Value *src = array_select(e, set, key);
if (src == NULL) {
assert(0); // TODO
}
r = *src;
} else if (type_of(set) == TYPE_MAP) {
int ret = map_select(e, set, key, &r);
if (ret < 0) {
assert(0); // TODO
}
} else {
eval_report(e, "Invalid selection from non-array and non-map value");
return -1;
}
e->eval_stack[e->eval_depth++] = r;
}
break;
case OPCODE_PRINT:
{
Value v = e->eval_stack[e->eval_depth-1];
value_print(v);
}
break;
default:
eval_report(e, "Invalid opcode (offset %d)", e->off-1);
return -1;
}
return 0;
}
int eval(Program p, Arena *a, char *errbuf, int errmax)
{
String code;
String data;
int ret = parse_program_header(p, &code, &data, errbuf, errmax);
if (ret < 0)
return -1;
Eval e = {
.code=code,
.data=data,
.off=0,
.a=a,
.errbuf=errbuf,
.errmax=errmax,
.errlen=0,
.num_frames=0,
.eval_depth=0,
.num_groups=0,
};
e.frames[e.num_frames++] = (Frame) { 0, 0 };
for (;;) {
int ret = step(&e);
if (ret < 0) return -1;
if (ret == 1) break;
}
e.num_frames--;
return 0;
}