static void list_put(struct context *context, enum Opcode op, bool really) { DEBUGPRINT("PUT\n"); if (!context->runtime) return; struct variable* recipient = variable_pop(context); struct variable* key = variable_pop(context); struct variable *value = get_value(context, op); if (!really && custom_method(context, RESERVED_SET, recipient, key, value)) return; switch (key->type) { case VAR_INT: switch (recipient->type) { case VAR_LST: array_set(recipient->list, key->integer, value); break; case VAR_STR: case VAR_BYT: byte_array_set(recipient->str, key->integer, value->integer); break; default: vm_exit_message(context, "indexing non-indexable"); } break; case VAR_STR: variable_map_insert(recipient, key->str, value); break; default: vm_exit_message(context, "bad index type"); break; } }
static void unary_op(struct context *context, enum Opcode op) { if (!context->runtime) VM_DEBUGPRINT("%s\n", NUM_TO_STRING(opcodes, op)); struct variable *v = (struct variable*)variable_pop(context); struct variable *result = NULL; switch (v->type) { case VAR_NIL: { switch (op) { case VM_NEG: result = variable_new_nil(context); break; case VM_NOT: result = variable_new_bool(context, true); break; default: vm_exit_message(context, "bad math operator"); break; } } break; case VAR_INT: { int32_t n = v->integer; switch (op) { case VM_NEG: result = variable_new_int(context, -n); break; case VM_NOT: result = variable_new_bool(context, !n); break; case VM_INV: result = variable_new_int(context, ~n); break; default: vm_exit_message(context, "bad math operator"); break; } } break; case VAR_FLT: { float n = v->floater; switch (op) { case VM_NEG: result = variable_new_float(context, -n); break; case VM_NOT: result = variable_new_bool(context, !n); break; default: vm_exit_message(context, "bad math operator"); break; } } break; default: if (op == VM_NOT) result = variable_new_bool(context, false); else vm_exit_message(context, "bad math type"); break; } variable_push(context, result); DEBUGPRINT("%s(%s) = %s\n", NUM_TO_STRING(opcodes, op), variable_value_str(context, v), variable_value_str(context, result)); }
static bool variable_compare(struct context *context, const struct variable *u, const struct variable *v) { if (!u != !v) return false; enum VarType ut = (enum VarType)u->type; enum VarType vt = (enum VarType)v->type; if (ut != vt) return false; switch (ut) { case VAR_LST: if (u->list->length != v->list->length) return false; for (int i=0; i<u->list->length; i++) { struct variable *ui = (struct variable*)array_get(u->list, i); struct variable *vi = (struct variable*)array_get(v->list, i); if (!variable_compare(context, ui, vi)) return false; } break; case VAR_BOOL: case VAR_INT: if (u->integer != v->integer) return false; break; case VAR_FLT: if (u->floater != v->floater) return false; break; case VAR_STR: if (!byte_array_equals(u->str, v->str)) return false; break; default: return (bool)vm_exit_message(context, "bad comparison"); } return variable_compare_maps(context, u->map, v->map); }
static struct variable *binary_op_int(struct context *context, enum Opcode op, const struct variable *u, const struct variable *v) { int32_t m = u->integer; int32_t n = v->integer; int32_t i; switch (op) { case VM_MUL: i = m * n; break; case VM_DIV: i = m / n; break; case VM_ADD: i = m + n; break; case VM_SUB: i = m - n; break; //case VM_AND: i = m && n; break; case VM_EQU: i = m == n; break; case VM_NEQ: i = m != n; break; //case VM_ORR: i = m || n; break; case VM_GTN: i = m > n; break; case VM_LTN: i = m < n; break; case VM_GRQ: i = m >= n; break; case VM_LEQ: i = m <= n; break; case VM_BND: i = m & n; break; case VM_BOR: i = m | n; break; case VM_MOD: i = m % n; break; case VM_XOR: i = m ^ n; break; case VM_RSF: i = m >> n; break; case VM_LSF: i = m << n; break; default: return (struct variable*)vm_exit_message(context, "bad math int operator"); } return variable_new_int(context, i); }
// get the indexed item and push on operand stack void lookup(struct context *context, struct variable *indexable, struct variable *index, bool really) { if (!really && custom_method(context, RESERVED_GET, indexable, index, NULL)) { return; } struct variable *item=0; switch (index->type) { case VAR_INT: item = list_get_int(context, indexable, index->integer); break; case VAR_STR: if (indexable->map) item = (struct variable*)map_get(indexable->map, index->str); if (!item) item = builtin_method(context, indexable, index); if (!item) item = variable_new_nil(context); break; case VAR_NIL: item = variable_new_nil(context); break; default: vm_exit_message(context, "bad index type"); break; } // DEBUGPRINT(" found %s\n", variable_value_str(context, item)); variable_push(context, item); }
int compar(struct context *context, const void *a, const void *b, struct variable *comparator) { struct variable *av = *(struct variable**)a; struct variable *bv = *(struct variable**)b; if (comparator) { byte_array_reset(comparator->str); vm_call(context, comparator, av, bv, NULL); struct variable *result = (struct variable*)stack_pop(context->operand_stack); if (result->type == VAR_SRC) result = array_get(result->list.ordered, 0); assert_message(result->type == VAR_INT, "non-integer comparison result"); return result->integer; } else { enum VarType at = av->type; enum VarType bt = bv->type; if (at == VAR_INT && bt == VAR_INT) { // DEBUGPRINT("compare %p:%d to %p:%d : %d\n", av, av->integer, bv, bv->integer, av->integer - bv->integer); return av->integer - bv->integer; } else DEBUGPRINT("can't compare %s to %s\n", var_type_str(at), var_type_str(bt)); vm_exit_message(context, "incompatible types for comparison"); return 0; } }
void vm_call_src(struct context *context, struct variable *func) { struct map *env = NULL; if (func->map) { struct variable *v = (struct variable*)variable_map_get(context, func, byte_array_from_string(RESERVED_ENV)); if (v) env = v->map; } struct program_state *state = (struct program_state*)stack_peek(context->program_stack, 0); struct variable *s = (struct variable*)stack_peek(context->operand_stack, 0); state->args = array_copy(s->list); INDENT // call the function switch (func->type) { case VAR_FNC: run(context, func->str, env, false); break; case VAR_C: { struct variable *v = func->cfnc(context); if (!v) v = variable_new_src(context, 0); else if (v->type != VAR_SRC) { // convert to VAR_SRC variable stack_push(context->operand_stack, v); v = variable_new_src(context, 1); } stack_push(context->operand_stack, v); // push the result } break; case VAR_NIL: vm_exit_message(context, "can't find function"); break; default: vm_exit_message(context, "not a function"); break; } state->args = NULL; UNDENT }
static struct variable *binary_op_str(struct context *context, enum Opcode op, struct variable *u, struct variable *v) { struct variable *w = NULL; struct byte_array *ustr = u->type == VAR_STR ? u->str : variable_value(context, u); struct byte_array *vstr = v->type == VAR_STR ? v->str : variable_value(context, v); switch (op) { case VM_ADD: w = variable_new_str(context, byte_array_concatenate(2, vstr, ustr)); break; case VM_EQU: w = variable_new_int(context, byte_array_equals(ustr, vstr)); break; default: return (struct variable*)vm_exit_message(context, "unknown string operation"); } return w; }
static struct variable *binary_op_lst(struct context *context, enum Opcode op, const struct variable *u, const struct variable *v) { vm_assert(context, u->type==VAR_LST && v->type==VAR_LST, "list op with non-lists"); struct variable *w = NULL; switch (op) { case VM_ADD: w = variable_copy(context, v); for (int i=0; i<u->list->length; i++) array_add(w->list, array_get(u->list, i)); map_update(w->map, u->map); break; default: return (struct variable*)vm_exit_message(context, "unknown string operation"); } return w; }
static struct variable *binary_op_nil(struct context *context, enum Opcode op, const struct variable *u, const struct variable *v) { vm_assert(context, u->type==VAR_NIL || v->type==VAR_NIL, "nil op with non-nils"); if (v->type == VAR_NIL && u->type != VAR_NIL) return binary_op_nil(context, op, v, u); // 1st var should be nil switch (op) { case VM_EQU: return variable_new_bool(context, v->type == u->type); case VM_NEQ: return variable_new_bool(context, v->type != u->type); case VM_ADD: case VM_SUB: return variable_copy(context, v); case VM_LTN: case VM_GTN: case VM_LEQ: case VM_GRQ: return variable_new_bool(context, false); default: return vm_exit_message(context, "unknown binary nil op"); } }
static struct variable *binary_op_float(struct context *context, enum Opcode op, const struct variable *u, const struct variable *v) { float m = u->floater; float n = v->floater; float f = 0; switch (op) { case VM_MUL: f = m * n; break; case VM_DIV: f = m / n; break; case VM_ADD: f = m + n; break; case VM_SUB: f = m - n; break; case VM_NEQ: f = m != n; break; case VM_GTN: return variable_new_int(context, n > m); case VM_LTN: return variable_new_int(context, n < m); case VM_GRQ: return variable_new_int(context, n >= m); case VM_LEQ: return variable_new_int(context, n <= m); default: return (struct variable*)vm_exit_message(context, "bad math float operator"); } return variable_new_float(context, f); }
static void binary_op(struct context *context, enum Opcode op) { if (!context->runtime) VM_DEBUGPRINT("%s\n", NUM_TO_STRING(opcodes, op)); struct variable *u = variable_pop(context); struct variable *v = variable_pop(context); enum VarType ut = (enum VarType)u->type; enum VarType vt = (enum VarType)v->type; struct variable *w; if (ut == VAR_NIL || vt == VAR_NIL) { w = binary_op_nil(context, op, u, v); } else if ((op == VM_EQU) || (op == VM_NEQ)) { bool same = variable_compare(context, u, v) ^ (op == VM_NEQ); w = variable_new_bool(context, same); } else { bool floater = (ut == VAR_FLT && is_num(vt)) || (vt == VAR_FLT && is_num(ut)); bool inter = (ut==VAR_INT || ut==VAR_BOOL) && (vt==VAR_INT || vt==VAR_BOOL); if (floater) w = binary_op_float(context, op, u, v); else if (inter) w = binary_op_int(context, op, v, u); else if (vt == VAR_STR || ut == VAR_STR) w = binary_op_str(context, op, u, v); else if (vt == VAR_LST) w = binary_op_lst(context, op, u, v); else vm_exit_message(context, "unknown binary op"); } variable_push(context, w); DEBUGPRINT("%s(%s,%s) = %s\n", NUM_TO_STRING(opcodes, op), variable_value_str(context, v), variable_value_str(context, u), variable_value_str(context, w)); }
static struct variable *list_get_int(struct context *context, const struct variable *indexable, uint32_t index) { null_check(indexable); enum VarType it = (enum VarType)indexable->type; switch (it) { case VAR_INT: return variable_new_int(context, index); case VAR_LST: if (index < indexable->list->length) return (struct variable*)array_get(indexable->list, index); return variable_new_nil(context); case VAR_STR: { vm_assert(context, index < indexable->str->length, "index out of bounds"); char *str = (char*)malloc(2); sprintf(str, "%c", indexable->str->data[index]); return variable_new_str(context, byte_array_from_string(str)); } default: vm_exit_message(context, "indexing non-indexable"); return NULL; } }
struct variable* variable_set(struct context *context, struct variable *dst, const struct variable* src) { vm_null_check(context, dst); vm_null_check(context, src); switch (src->type) { case VAR_NIL: break; case VAR_BOOL: dst->boolean = src->boolean; break; case VAR_INT: dst->integer = src->integer; break; case VAR_FLT: dst->floater = src->floater; break; case VAR_C: dst->cfnc = src->cfnc; break; case VAR_FNC: case VAR_BYT: case VAR_STR: dst->str = byte_array_copy(src->str); break; case VAR_MAP: dst->map = src->map; break; case VAR_SRC: case VAR_LST: dst->list = src->list; break; default: vm_exit_message(context, "bad var type"); break; } dst->map = src->map; dst->type = src->type; return dst; }
bool run(struct context *context, struct byte_array *program, struct map *env, bool in_context) { null_check(context); null_check(program); program = byte_array_copy(program); program->current = program->data; struct program_state *state = NULL; enum Opcode inst = VM_NIL; if (context->runtime) { if (in_context) { if (!state) state = (struct program_state*)stack_peek(context->program_stack, 0); env = state->named_variables; // use the caller's variable set in the new state } else state = program_state_new(context, env); } while (program->current < program->data + program->length) { inst = (enum Opcode)*program->current; bool really = inst & VM_RLY; inst &= ~VM_RLY; #ifdef DEBUG display_program_counter(context, program); #endif program->current++; // increment past the instruction int32_t pc_offset = 0; switch (inst) { case VM_COM: case VM_ITR: if (iterate(context, inst, state, program)) goto done; break; case VM_RET: if (ret(context, program)) goto done; break; case VM_TRO: if (tro(context)) goto done; break; case VM_TRY: if (vm_trycatch(context, program)) goto done; break; case VM_EQU: case VM_MUL: case VM_DIV: case VM_ADD: case VM_SUB: case VM_NEQ: case VM_GTN: case VM_LTN: case VM_GRQ: case VM_LEQ: case VM_BND: case VM_BOR: case VM_MOD: case VM_XOR: case VM_INV: case VM_RSF: case VM_LSF: binary_op(context, inst); break; case VM_ORR: case VM_AND: pc_offset = boolean_op(context, program, inst); break; case VM_NEG: case VM_NOT: unary_op(context, inst); break; case VM_SRC: src(context, inst, program); break; case VM_DST: dst(context, really); break; case VM_STX: case VM_SET: set(context, inst, state, program); break; case VM_JMP: pc_offset = jump(context, program); break; case VM_IFF: pc_offset = iff(context, program); break; case VM_CAL: func_call(context, inst, program, NULL); break; case VM_LST: push_list(context, program); break; case VM_MAP: push_map(context, program); break; case VM_NIL: push_nil(context); break; case VM_INT: push_int(context, program); break; case VM_FLT: push_float(context, program); break; case VM_BUL: push_bool(context, program); break; case VM_STR: push_str(context, program); break; case VM_VAR: push_var(context, program); break; case VM_FNC: push_fnc(context, program); break; case VM_GET: list_get(context, really); break; case VM_PTX: case VM_PUT: list_put(context, inst, really); break; case VM_MET: method(context, program, really); break; default: vm_exit_message(context, ERROR_OPCODE); return false; } program->current += pc_offset; } if (!context->runtime) return false; done: if (!in_context) stack_pop(context->program_stack); return inst == VM_RET; }