static void ffi_ptr_dereference_assign(VMState *state, CallInfo *info) { VM_ASSERT(info->args_len == 3, "wrong arity: expected 2, got %i", info->args_len); Object *root = state->root; Object *pointer_base = state->shared->vcache.pointer_base; FFIObject *ffi = (FFIObject*) AS_OBJ(OBJECT_LOOKUP(root, ffi)); Object *ffi_type = AS_OBJ(OBJECT_LOOKUP((Object*) ffi, type)); Object *thisptr = AS_OBJ(load_arg(state->frame, info->this_arg)); VM_ASSERT(thisptr->parent == pointer_base, "internal error"); PointerObject *thisptr_obj = (PointerObject*) thisptr; Object *ffi_type_obj = obj_instance_of(OBJ_OR_NULL(load_arg(state->frame, INFO_ARGS_PTR(info)[0])), ffi_type); Value offs_val = load_arg(state->frame, INFO_ARGS_PTR(info)[1]); VM_ASSERT(IS_INT(offs_val), "offset must be integer"); int offs = AS_INT(offs_val); VM_ASSERT(ffi_type_obj, "type is not a FFI type"); char *offset_ptr = (char*) thisptr_obj->ptr + offs; Value value = load_arg(state->frame, INFO_ARGS_PTR(info)[2]); (void) offset_ptr; (void) value; if (ffi_type_obj == ffi->long_obj) { VM_ASSERT(IS_INT(value), "can only assign integer to long"); *(long*) offset_ptr = AS_INT(value); } else { fprintf(stderr, "TODO\n"); abort(); } }
cullResult_t r_cull_bbox(ac_vec4_t bounds[2]) { ac_vec4_t v; int i, x, y, z; bool intersect = false; for (i = 0; i < 6; i++) { // floating point magic! extract the sign bits #define AS_INT(f) (*(int *)(&(f))) x = (AS_INT(r_frustum[i].f[0]) & 0x80000000) >> 31; y = (AS_INT(r_frustum[i].f[1]) & 0x80000000) >> 31; z = (AS_INT(r_frustum[i].f[2]) & 0x80000000) >> 31; #undef AS_INT // test the negative far point against the plane v = ac_vec_set( bounds[1 - x].f[0], bounds[1 - y].f[1], bounds[1 - z].f[2], 0); if (ac_vec_dot(v, r_frustum[i]) < r_frustum[i].f[3]) // negative far point behind plane -> box outside frustum return CR_OUTSIDE; // test the positive far point against the plane v = ac_vec_set( bounds[x].f[0], bounds[y].f[1], bounds[z].f[2], 0); if (ac_vec_dot(v, r_frustum[i]) < r_frustum[i].f[3]) intersect = true; } return intersect ? CR_INTERSECT : CR_INSIDE; }
/** * Finds the minimum value in a list. * * @param args list of args * @param c number of args * @returns min number in list */ LuciObject *luci_min(LuciObject **args, unsigned int c) { if (c < 1) { LUCI_DIE("%s", "Missing parameter to min()\n"); } LuciObject *list = args[0]; if (!list || (!ISTYPE(list, obj_list_t))) { LUCI_DIE("%s", "Must specify a list to calculate min\n"); } LuciObject *item; double min = 0; unsigned int i, found_float = 0; for (i = 0; i < AS_LIST(list)->count; i ++) { item = AS_LIST(list)->items[i]; if (!item) { LUCI_DIE("%s", "Can't calulate max of list containing NULL value\n"); } if (ISTYPE(item, obj_int_t)) { if (i == 0) { min = (double)AS_INT(item)->i; } else if ( (double)AS_INT(item)->i < min) { min = (double)AS_INT(item)->i; } } else if (ISTYPE(item, obj_float_t)) { found_float = 1; if (i == 0) { min = AS_FLOAT(item)->f; } else if (AS_FLOAT(item)->f < min) { min = AS_FLOAT(item)->f; } } else { LUCI_DIE("Can't find min of list containing an object of type %s\n", item->type->type_name); } } LuciObject *ret; if (!found_float) { ret = LuciInt_new((long)min); } else { ret = LuciFloat_new(min); } return ret; }
bool condition_op_const::eval(record_type& rt, record& r){ if (offset==-1){ offset = offset_of_column(rt, lhs_table_name, lhs_column_name); if (offset==-1) throw string("Undefined column"); } if (index==-1){ index = index_of_column(rt, lhs_table_name, lhs_column_name); if (index==-1) throw string("Undefined column"); } if (rt[index].type == column_type::INT){ if (op=="<"){ return AS_INT(r[offset]) < value.vInt; } else if (op=="="){ return AS_INT(r[offset]) == value.vInt; } else if (op==">"){ return AS_INT(r[offset]) > value.vInt; } throw string("unimplemented operators in condition_op_const"); } else if(rt[index].type == column_type::FLOAT){ if (op=="<"){ return AS_FLOAT(r[offset]) < value.vFloat; } else if (op=="="){ return AS_FLOAT(r[offset]) == value.vFloat; } else if (op==">"){ return AS_FLOAT(r[offset]) > value.vFloat; } throw string("unimplemented operators in condition_op_const"); } else if(rt[index].type == column_type::STRING){ if (op=="<"){ return (strncmp(AS_STRING(r[offset]), value.vString, 500) < 0); } else if (op=="="){ return (strncmp(AS_STRING(r[offset]), value.vString, 500) == 0); } else if (op==">"){ return (strncmp(AS_STRING(r[offset]), value.vString, 500) > 0); } throw string("unimplemented operators in condition_op_const"); } //throw string("unimplemented datatypes in condition_op_const"); }
/** * Casts a LuciObject to a LuciFloatObj if possible, then returns * the new object. * * @param args list of args * @param c number of args * @returns LuciFloatObj cast of the first arg */ LuciObject *luci_cast_float(LuciObject **args, unsigned int c) { LuciObject *ret = LuciNilObj; if (c < 1) { LUCI_DIE("%s", "Missing parameter to int()\n"); } LuciObject *item = args[0]; if (!item) { LUCI_DIE("%s", "Can't cast NULL to int\n"); } if (ISTYPE(item, obj_int_t)) { ret = LuciFloat_new((double)AS_INT(item)->i); } else if (ISTYPE(item, obj_float_t)) { ret = LuciFloat_new(AS_FLOAT(item)->f); } else if (ISTYPE(item, obj_string_t)) { double f; int scanned = sscanf(AS_STRING(item)->s, "%f", (float *)&f); if (scanned <= 0 || scanned == EOF) { LUCI_DIE("%s", "Could not cast to float\n"); } ret = LuciFloat_new(f); } else { LUCI_DIE("Cannot cast type %s to type float\n", item->type->type_name); } return ret; }
/** * Asserts that a given LuciObject is equivalent to a boolean True * * Currently uses C @code assert @endcode , which will exit a program * mid-execution if the assertion fails. * * @param args list of args * @param c number of args * @returns LuciNilObj */ LuciObject *luci_assert(LuciObject **args, unsigned int c) { if (c < 1) { LUCI_DIE("%s", "Missing condition parameter to assert()\n"); } LuciObject *item = args[0]; if (ISTYPE(item, obj_int_t) && !AS_INT(item)->i) { LUCI_DIE("%s\n", "Assertion failed"); } else if (ISTYPE(item, obj_float_t) && !((long)AS_FLOAT(item)->f)) { LUCI_DIE("%s\n", "Float assertion failed"); } else if (ISTYPE(item, obj_string_t)) { if (strcmp("", AS_STRING(item)->s) == 0) { LUCI_DIE("%s\n", "String assertion failed"); } } else if (ISTYPE(item, obj_list_t) && (AS_LIST(item)->count == 0)) { LUCI_DIE("%s\n", "List assertion failed"); } else if (ISTYPE(item, obj_map_t) && (AS_MAP(item)->count == 0)) { LUCI_DIE("%s\n", "Map assertion failed"); } else if (ISTYPE(item, obj_file_t) && (AS_FILE(item)->ptr)) { LUCI_DIE("%s\n", "File assertion failed"); } return LuciNilObj; }
static void ffi_ptr_dereference(VMState *state, CallInfo *info) { VM_ASSERT(info->args_len == 2, "wrong arity: expected 2, got %i", info->args_len); Object *root = state->root; Object *pointer_base = state->shared->vcache.pointer_base; FFIObject *ffi = (FFIObject*) AS_OBJ(OBJECT_LOOKUP(root, ffi)); Object *ffi_type = AS_OBJ(OBJECT_LOOKUP((Object*) ffi, type)); Object *thisptr = AS_OBJ(load_arg(state->frame, info->this_arg)); VM_ASSERT(thisptr->parent == pointer_base, "internal error"); PointerObject *thisptr_obj = (PointerObject*) thisptr; Object *ffi_type_obj = obj_instance_of(OBJ_OR_NULL(load_arg(state->frame, INFO_ARGS_PTR(info)[0])), ffi_type); Value offs_val = load_arg(state->frame, INFO_ARGS_PTR(info)[1]); VM_ASSERT(IS_INT(offs_val), "offset must be integer"); int offs = AS_INT(offs_val); VM_ASSERT(ffi_type_obj, "type is not a FFI type"); char *offset_ptr = (char*) thisptr_obj->ptr + offs; if (ffi_type_obj == ffi->short_obj) { short s = *(short*) offset_ptr; vm_return(state, info, INT2VAL(s)); } else if (ffi_type_obj == ffi->ushort_obj) { unsigned short us = *(unsigned short*) offset_ptr; vm_return(state, info, INT2VAL(us)); } else if (ffi_type_obj == ffi->int_obj) { int i = *(int*) offset_ptr; vm_return(state, info, INT2VAL(i)); } else if (ffi_type_obj == ffi->uint_obj) { unsigned int u = *(unsigned int*) offset_ptr; vm_return(state, info, INT2VAL(u)); } else if (ffi_type_obj == ffi->int8_obj) { int8_t i8 = *(int8_t*) offset_ptr; vm_return(state, info, INT2VAL(i8)); } else if (ffi_type_obj == ffi->uint8_obj) { uint8_t u8 = *(uint8_t*) offset_ptr; vm_return(state, info, INT2VAL(u8)); } else if (ffi_type_obj == ffi->int32_obj) { int32_t i32 = *(int32_t*) offset_ptr; vm_return(state, info, INT2VAL(i32)); } else if (ffi_type_obj == ffi->uint32_obj) { uint32_t u32 = *(uint32_t*) offset_ptr; vm_return(state, info, INT2VAL(u32)); } else if (ffi_type_obj == ffi->pointer_obj) { void *ptr = *(void**) offset_ptr; vm_return(state, info, make_ffi_pointer(state, ptr)); } else if (ffi_type_obj == ffi->char_pointer_obj) { char *ptr = *(char**) offset_ptr; vm_return(state, info, make_string_static(state, ptr)); } else if (ffi_type_obj == ffi->long_obj) { long l = *(long*) offset_ptr; if (l < INT_MIN || l > INT_MAX) { VM_ASSERT(false, "value exceeds bounds of my int type"); } vm_return(state, info, INT2VAL((int) l)); } else { fprintf(stderr, "TODO\n"); abort(); } }
/** * Returns the next LuciObject in a container. * * @param iterator from which to compute next object * @returns next object in iterator's sequence or NULL if finished iterating */ LuciObject *iterator_next_object(LuciObject *iterator) { if (!iterator || (!ISTYPE(iterator, obj_iterator_t))) { LUCI_DIE("%s", "Can't get next from non-iterator object\n"); } LuciIteratorObj *iter = (LuciIteratorObj *)iterator; LuciObject *container = iter->container; LuciObject *next = container->type->next(container, iter->idx); AS_INT(iter->idx)->i += iter->step; return next; }
static void ffi_ptr_index_assign_fn(VMState *state, CallInfo *info) { VM_ASSERT(info->args_len == 2, "wrong arity: expected 2, got %i", info->args_len); Object *pointer_base = state->shared->vcache.pointer_base; Object *thisptr = OBJ_OR_NULL(load_arg(state->frame, info->this_arg)); VM_ASSERT(thisptr && thisptr->parent == pointer_base, "invalid pointer index write on non-pointer object"); PointerObject *thisptr_obj = (PointerObject*) thisptr; Object *ffi_type_obj = AS_OBJ(OBJECT_LOOKUP(thisptr, target_type)); VM_ASSERT(ffi_type_obj, "cannot assign index on untyped pointer!"); Value offs_val = load_arg(state->frame, INFO_ARGS_PTR(info)[0]); VM_ASSERT(IS_INT(offs_val), "offset must be integer"); int offs = AS_INT(offs_val); Value sizeof_val = OBJECT_LOOKUP(ffi_type_obj, sizeof); VM_ASSERT(IS_INT(sizeof_val), "internal error: sizeof wrong type or undefined"); int elemsize = AS_INT(sizeof_val); char *offset_ptr = (char*) thisptr_obj->ptr + elemsize * offs; bool res = ffi_pointer_write(state, ffi_type_obj, (void*) offset_ptr, load_arg(state->frame, INFO_ARGS_PTR(info)[1])); if (!res) return; }
/** * Returns a hex string representation of an integer * * @param args list of args * @param c number of args * @returns LuciStringObj representation of an integer */ LuciObject *luci_hex(LuciObject **args, unsigned int c) { if (c < 1) { LUCI_DIE("%s\n", "Missing param to hex()"); } LuciObject *hexint = args[0]; if (!ISTYPE(hexint, obj_int_t)) { LUCI_DIE("Cannot get hex representation of an object of type %s\n", hexint->type->type_name); } char *s = alloc(MAX_INT_DIGITS + 2); snprintf(s, MAX_INT_DIGITS, "0x%lX", AS_INT(hexint)->i); return LuciString_new(s); }
static void ffi_ptr_add(VMState *state, CallInfo *info) { VM_ASSERT(info->args_len == 1, "wrong arity: expected 1, got %i", info->args_len); Object *pointer_base = state->shared->vcache.pointer_base; Object *thisptr = OBJ_OR_NULL(load_arg(state->frame, info->this_arg)); VM_ASSERT(thisptr && thisptr->parent == pointer_base, "internal error"); PointerObject *thisptr_obj = (PointerObject*) thisptr; void *ptr = (void*) thisptr_obj->ptr; int elemsize = 1; Value target_type = OBJECT_LOOKUP(thisptr, target_type); if (!IS_NULL(target_type)) { VM_ASSERT(IS_OBJ(target_type), "target type must be ffi type"); Value sizeof_val = OBJECT_LOOKUP(AS_OBJ(target_type), sizeof); VM_ASSERT(IS_INT(sizeof_val), "internal error: sizeof wrong type or undefined"); elemsize = AS_INT(sizeof_val); }
bool ffi_pointer_write(VMState *state, Object *type, void *ptr, Value val) { ValueCache *vcache = &state->shared->vcache; Object *string_base = vcache->string_base; FFIObject *ffi = (FFIObject*) vcache->ffi_obj; if (type == ffi->float_obj) { if (IS_FLOAT(val)) *(float*) ptr = AS_FLOAT(val); else if (IS_INT(val)) *(float*) ptr = AS_INT(val); else { VM_ASSERT(false, "invalid value for float type") false; } return true; } else { Object *c_type_obj = AS_OBJ(OBJECT_LOOKUP(type, c_type)); StringObject *c_type = (StringObject*) obj_instance_of(c_type_obj, string_base); assert(c_type); VM_ASSERT(false, "unhandled pointer write type: %s", c_type->value) false; } }
/** * Returns a boolean representation of a LuciIteratorObj * * @param o LuciIteratorObj * @returns true if the iterator can continue to iterate */ LuciObject* LuciIterator_asbool(LuciObject *o) { LuciObject *res = LuciNilObj; LuciObject *container = AS_ITERATOR(o)->container; unsigned int len = 0; if (ISTYPE(container, obj_list_t)) { len = AS_LIST(container)->count; } else if (ISTYPE(container, obj_map_t)) { len = AS_LIST(container)->size; } if (AS_INT(AS_ITERATOR(o)->idx)->i < len) { res = LuciInt_new(true); } else { res = LuciInt_new(false); } return res; }
/** * Computes the sum of a range of numbers. * * @param args list of args * @param c number of args * @returns sum of numbers */ LuciObject * luci_sum(LuciObject **args, unsigned int c) { if (c < 1) { LUCI_DIE("%s", "Missing parameter to sum()\n"); } LuciObject *list = args[0]; if (!list || (!ISTYPE(list, obj_list_t))) { LUCI_DIE("%s", "Must specify a list to calculate sum\n"); } LuciObject *item; double sum = 0; unsigned int i, found_float = 0; for (i = 0; i < AS_LIST(list)->count; i++) { item = AS_LIST(list)->items[i]; if (!item) { LUCI_DIE("%s", "Can't calulate sum of list containing NULL value\n"); } if (ISTYPE(item, obj_int_t)) { sum += (double)AS_INT(item)->i; } else if (ISTYPE(item, obj_float_t)) { found_float = 1; sum += AS_FLOAT(item)->f; } else { LUCI_DIE("%s", "Can't calculate sum of list containing non-numeric value\n"); } } LuciObject *ret; if (!found_float) { ret = LuciInt_new((long)sum); } else { ret = LuciFloat_new(sum); } return ret; }
PointerObject *thisptr_obj = (PointerObject*) thisptr; void *ptr = (void*) thisptr_obj->ptr; int elemsize = 1; Value target_type = OBJECT_LOOKUP(thisptr, target_type); if (!IS_NULL(target_type)) { VM_ASSERT(IS_OBJ(target_type), "target type must be ffi type"); Value sizeof_val = OBJECT_LOOKUP(AS_OBJ(target_type), sizeof); VM_ASSERT(IS_INT(sizeof_val), "internal error: sizeof wrong type or undefined"); elemsize = AS_INT(sizeof_val); } Value offset_val = load_arg(state->frame, INFO_ARGS_PTR(info)[0]); VM_ASSERT(IS_INT(offset_val), "offset must be integer"); int offset = AS_INT(offset_val); vm_return(state, info, make_ffi_pointer(state, (void*) ((char*)ptr + offset * elemsize))); } static Value make_ffi_pointer(VMState *state, void *ptr) { Object *ptr_obj = AS_OBJ(make_ptr(state, ptr)); OBJECT_SET(state, ptr_obj, dereference, make_fn(state, ffi_ptr_dereference)); OBJECT_SET(state, ptr_obj, dereference_assign, make_fn(state, ffi_ptr_dereference_assign)); OBJECT_SET(state, ptr_obj, __add, make_fn(state, ffi_ptr_add)); OBJECT_SET(state, ptr_obj, target_type, VNULL); OBJECT_SET(state, ptr_obj, __slice, make_fn(state, ffi_ptr_index_fn)); OBJECT_SET(state, ptr_obj, __slice_assign, make_fn(state, ffi_ptr_index_assign_fn)); return OBJ2VAL(ptr_obj); }
void execute_function(VM *vm) { restart: { Frame *frame = vm->current; Closure *closure = frame->closure; Chunk *chunk = closure->chunk; StackObject *registers = frame->registers; while (frame->pc < chunk->numinstructions) { int instruction = chunk->instructions[frame->pc]; OpCode o = GET_O(instruction); int a = GET_A(instruction); int b = GET_B(instruction); int c = GET_C(instruction); switch (o) { case OP_MOVE: { if (b < 256) { copy_object(®isters[a], ®isters[b]); } else { copy_constant(vm, ®isters[a], chunk->constants[b - 256]); } } break; case OP_GETUPVAR: { Upval *upval = closure->upvals[b]; if (!upval->open) { // upval is closed copy_object(®isters[a], upval->data.o); } else { // still on stack copy_object(®isters[a], &upval->data.ref.frame->registers[upval->data.ref.slot]); } } break; case OP_SETUPVAR: { Upval *upval = closure->upvals[b]; if (!upval->open) { // upval is closed copy_object(upval->data.o, ®isters[a]); } else { // still on stack copy_object(&upval->data.ref.frame->registers[upval->data.ref.slot], ®isters[a]); } } break; case OP_ADD: { // TODO - make string coercion better // TODO - make string type with special operators if (IS_STR(b) || IS_STR(c)) { char *arg1 = TO_STR(b); char *arg2 = TO_STR(c); char *arg3 = malloc((strlen(arg1) + strlen(arg2) + 1) + sizeof *arg3); strcpy(arg3, arg1); strcat(arg3, arg2); registers[a].value.o = make_string_ref(vm, arg3); registers[a].type = OBJECT_REFERENCE; // put this after free(arg1); free(arg2); } else { if (!(IS_INT(b) || IS_REAL(b)) || !(IS_INT(c) || IS_REAL(c))) { fatal("Cannot add types."); } if (IS_INT(b) && IS_INT(c)) { int arg1 = AS_INT(b); int arg2 = AS_INT(c); registers[a].type = OBJECT_INT; registers[a].value.i = arg1 + arg2; } else { double arg1 = IS_INT(b) ? (double) AS_INT(b) : AS_REAL(b); double arg2 = IS_INT(c) ? (double) AS_INT(c) : AS_REAL(c); registers[a].type = OBJECT_REAL; registers[a].value.d = arg1 + arg2; } } } break; case OP_SUB: { if (!(IS_INT(b) || IS_REAL(b)) || !(IS_INT(c) || IS_REAL(c))) { fatal("Tried to sub non-numbers."); } if (IS_INT(b) && IS_INT(c)) { int arg1 = AS_INT(b); int arg2 = AS_INT(c); registers[a].type = OBJECT_INT; registers[a].value.i = arg1 - arg2; } else { double arg1 = IS_INT(b) ? (double) AS_INT(b) : AS_REAL(b); double arg2 = IS_INT(c) ? (double) AS_INT(c) : AS_REAL(c); registers[a].type = OBJECT_REAL; registers[a].value.d = arg1 - arg2; } } break; case OP_MUL: { if (!(IS_INT(b) || IS_REAL(b)) || !(IS_INT(c) || IS_REAL(c))) { fatal("Tried to mul non-numbers."); } if (IS_INT(b) && IS_INT(c)) { int arg1 = AS_INT(b); int arg2 = AS_INT(c); registers[a].type = OBJECT_INT; registers[a].value.i = arg1 * arg2; } else { double arg1 = IS_INT(b) ? (double) AS_INT(b) : AS_REAL(b); double arg2 = IS_INT(c) ? (double) AS_INT(c) : AS_REAL(c); registers[a].type = OBJECT_REAL; registers[a].value.d = arg1 * arg2; } } break; case OP_DIV: { if (!(IS_INT(b) || IS_REAL(b)) || !(IS_INT(c) || IS_REAL(c))) { fatal("Tried to div non-numbers."); } if ((IS_INT(c) && AS_INT(c) == 0) || (IS_REAL(c) && AS_REAL(c) == 0)) { fatal("Div by 0."); } if (IS_INT(b) && IS_INT(c)) { int arg1 = AS_INT(b); int arg2 = AS_INT(c); registers[a].type = OBJECT_INT; registers[a].value.i = arg1 / arg2; } else { double arg1 = IS_INT(b) ? (double) AS_INT(b) : AS_REAL(b); double arg2 = IS_INT(c) ? (double) AS_INT(c) : AS_REAL(c); registers[a].type = OBJECT_REAL; registers[a].value.d = arg1 / arg2; } } break; case OP_MOD: { if (!(IS_INT(b) || IS_REAL(b)) || !(IS_INT(c) || IS_REAL(c))) { fatal("Tried to div non-numbers."); } if ((IS_INT(c) && AS_INT(c) == 0) || (IS_REAL(c) && AS_REAL(c) == 0)) { fatal("Mod by 0."); } if (IS_INT(b) && IS_INT(c)) { int arg1 = AS_INT(b); int arg2 = AS_INT(c); registers[a].type = OBJECT_INT; registers[a].value.i = arg1 % arg2; } else { double arg1 = IS_INT(b) ? (double) AS_INT(b) : AS_REAL(b); double arg2 = IS_INT(c) ? (double) AS_INT(c) : AS_REAL(c); registers[a].type = OBJECT_REAL; registers[a].value.i = fmod(arg1, arg2); } } break; case OP_POW: { if (!(IS_INT(b) || IS_REAL(b)) || !(IS_INT(c) || IS_REAL(c))) { fatal("Tried to div non-numbers."); } if (IS_INT(b) && IS_INT(c)) { int arg1 = AS_INT(b); int arg2 = AS_INT(c); registers[a].type = OBJECT_INT; registers[a].value.i = (int) pow(arg1, arg2); } else { double arg1 = IS_INT(b) ? (double) AS_INT(b) : AS_REAL(b); double arg2 = IS_INT(c) ? (double) AS_INT(c) : AS_REAL(c); registers[a].type = OBJECT_REAL; registers[a].value.d = pow(arg1, arg2); } } break; case OP_NEG: { if (IS_INT(b)) { registers[a].type = OBJECT_INT; registers[a].value.i = -AS_INT(b); } else if (IS_REAL(b)) { registers[a].type = OBJECT_INT; registers[a].value.i = -AS_REAL(b); } else { fatal("Tried to negate non-numeric type."); } } break; case OP_NOT: { if (registers[a].type != OBJECT_BOOL) { fatal("Expected boolean type, not %d.", registers[a].type); } registers[a].value.i = registers[a].value.i == 1 ? 0 : 1; } break; case OP_EQ: { if ((IS_INT(b) || IS_REAL(b)) && (IS_INT(c) || IS_REAL(c))) { double arg1 = IS_INT(b) ? (double) AS_INT(b) : AS_REAL(b); double arg2 = IS_INT(c) ? (double) AS_INT(c) : AS_REAL(c); registers[a].type = OBJECT_BOOL; registers[a].value.i = arg1 == arg2; } else { fatal("Comparison of reference types not yet supported."); } } break; case OP_LT: { if (!(IS_INT(b) || IS_REAL(b)) || !(IS_INT(c) || IS_REAL(c))) { fatal("Tried to compare non-numbers."); } double arg1 = IS_INT(b) ? (double) AS_INT(b) : AS_REAL(b); double arg2 = IS_INT(c) ? (double) AS_INT(c) : AS_REAL(c); registers[a].type = OBJECT_BOOL; registers[a].value.i = arg1 < arg2; } break; case OP_LE: { if (!(IS_INT(b) || IS_REAL(b)) || !(IS_INT(c) || IS_REAL(c))) { fatal("Tried to compare non-numbers."); } double arg1 = IS_INT(b) ? (double) AS_INT(b) : AS_REAL(b); double arg2 = IS_INT(c) ? (double) AS_INT(c) : AS_REAL(c); registers[a].type = OBJECT_BOOL; registers[a].value.i = arg1 <= arg2; } break; case OP_CLOSURE: { Closure *child = make_closure(chunk->children[b]); int i; for (i = 0; i < chunk->children[b]->numupvars; i++) { int inst = chunk->instructions[++frame->pc]; OpCode oc = GET_O(inst); int ac = GET_A(inst); int bc = GET_B(inst); int cc = GET_C(inst); if (oc == OP_MOVE) { // first upval for this variable child->upvals[ac] = make_upval(vm, bc); } else { // share upval child->upvals[ac] = closure->upvals[bc]; child->upvals[ac]->refcount++; } } registers[a].value.o = make_closure_ref(vm, child); registers[a].type = OBJECT_REFERENCE; // put this after } break; case OP_CALL: { if (registers[b].type != OBJECT_REFERENCE || registers[b].value.o->type != OBJECT_CLOSURE) { fatal("Tried to call non-closure."); } // TODO - safety issue (see compile.c for notes) Closure *child = registers[b].value.o->value.c; Frame *subframe = make_frame(frame, child); int i; for (i = 0; i < child->chunk->numparams; i++) { copy_object(&subframe->registers[i + 1], ®isters[c + i]); } vm->current = subframe; goto restart; } break; case OP_RETURN: { UpvalNode *head; for (head = vm->open; head != NULL; ) { Upval *u = head->upval; if (u->data.ref.frame == frame) { StackObject *o = malloc(sizeof *o); if (!o) { fatal("Out of memory."); } u->open = 0; copy_object(o, ®isters[u->data.ref.slot]); u->data.o = o; if (vm->open == head) { vm->open = head->next; } else { head->next->prev = head->prev; head->prev->next = head->next; } UpvalNode *temp = head; head = head->next; free(temp); } else { head = head->next; } } if (vm->current->parent != NULL) { Frame *p = vm->current->parent; StackObject *target = &p->registers[GET_A(p->closure->chunk->instructions[p->pc++])]; if (b < 256) { // debug char *d = obj_to_str(®isters[b]); printf("Return value: %s\n", d); free(d); copy_object(target, ®isters[b]); } else { copy_constant(vm, target, chunk->constants[b - 256]); } free_frame(frame); vm->current = p; goto restart; } else { // debug char *d = obj_to_str(®isters[b]); printf("Return value: %s\n", d); free(d); free_frame(frame); vm->current = NULL; return; } } break; case OP_JUMP: frame->pc += c ? -b : b; break; case OP_JUMP_TRUE: { if (registers[a].type != OBJECT_BOOL) { fatal("Expected boolean type, not %d.", registers[a].type); } if (registers[a].value.i == 1) { frame->pc += c ? -b : b; } } break; case OP_JUMP_FALSE: { if (registers[a].type != OBJECT_BOOL) { fatal("Expected boolean type, not %d.", registers[a].type); } if (registers[a].value.i == 0) { frame->pc += c ? -b : b; } } break; case OP_ENTER_TRY: { vm->catchframe = make_catch_frame(frame, vm->catchframe, frame->pc + b); } break; case OP_LEAVE_TRY: { CatchFrame *temp = vm->catchframe; vm->catchframe = vm->catchframe->parent; free_catch_frame(temp); } break; case OP_THROW: { // TODO - replace unwinding of stack with an exceptions // table per-chunk. It will have an instructions range, // the starting instruction of a handler, and the type of // exception that it may handle. // Exception table: // From To Target Type // 0 4 5 Class TestExc1 // 0 12 12 Class TestExc2 // TODO - implement a way to expect an exception // of a given type instead of a generic catch-all. char *s = obj_to_str(®isters[a]); printf("Exception value: %s!\n", s); free(s); // TODO - this is probably wrong. Not sure how complicated // it will be to handle upvalues and frame destruction here, // so we're just doing it a shitty way for now :D [GO LAZE]. if (!vm->catchframe) { // TODO - print a stack trace [ requires debug symbols :( ] fatal("Exception thrown outside of handler."); } while (vm->current != vm->catchframe->frame) { // TODO - destruct frame vm->current = vm->current->parent; } vm->current->pc = vm->catchframe->target; CatchFrame *temp = vm->catchframe; vm->catchframe = vm->catchframe->parent; free_catch_frame(temp); goto restart; } break; } frame->pc++; } fatal("VM left instruction-space."); } }
/** * Creates a LuciListObj containing a range of numbers. * * @param args list of args * @param c number of args * @returns list of numbers */ LuciObject * luci_range(LuciObject **args, unsigned int c) { long start, end, incr; LuciObject *first, *second, *third; if (c < 1) { LUCI_DIE("%s", "Missing parameter to range()\n"); } first = args[0]; if (!ISTYPE(first, obj_int_t)) { LUCI_DIE("%s", "First parameter to range must be integer\n"); } if (c > 1) { second = args[1]; if (!ISTYPE(second, obj_int_t)) { LUCI_DIE("%s", "Second parameter to range must be integer\n"); } start = AS_INT(first)->i; end = AS_INT(second)->i; if (c > 2) { /* Ternary range(X, Y, Z) call */ third = args[2]; if (!ISTYPE(third, obj_int_t)) { LUCI_DIE("%s", "Third parameter to range must be integer\n"); } incr = AS_INT(third)->i; } else { incr = 1; } } else { /* Basic range(X) call, increment by 1 starting from 0 */ start = 0; end = AS_INT(first)->i; incr = 1; } /* Build a list of integers from start to end, incrementing by incr */ LuciObject *item, *list = LuciList_new(); long i; if (incr < 0) { if (start <= end) { /* return empty list if idiotically requested */ return list; } for (i = start; i > end; i += incr) { item = LuciInt_new(i); LuciList_append(list, item); } } else { if (start >= end) { /* return empty list if idiotically requested */ return list; } for (i = start; i < end; i += incr) { item = LuciInt_new(i); LuciList_append(list, item); } } return list; }