#include #include #include #include #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; }