// 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); }
static inline struct variable *cfnc_chop(struct context *context, bool snip) { int32_t beginning, foraslongas; struct variable *args = (struct variable*)stack_pop(context->operand_stack); struct variable *self = (struct variable*)array_get(args->list.ordered, 0); enum VarType type = self->type; if (type == VAR_NIL) return variable_new_nil(context); assert_message(self->type == VAR_LST || self->type == VAR_STR, "can't chop"); if (args->list.ordered->length > 1) { struct variable *start = (struct variable*)array_get(args->list.ordered, 1); assert_message(start->type == VAR_INT, "non-integer index"); beginning = start->integer; } else beginning = 0; if (args->list.ordered->length > 2) { struct variable *length = (struct variable*)array_get(args->list.ordered, 2); assert_message(length->type == VAR_INT, "non-integer length"); foraslongas = length->integer; } else foraslongas = snip ? 1 : self->str->length - beginning; struct variable *result = variable_part(context, self, beginning, foraslongas); if (snip) variable_remove(self, beginning, foraslongas); return result; }
struct variable *sys_load(struct context *context) { struct variable *value = (struct variable*)stack_pop(context->operand_stack); struct variable *path = (struct variable*)array_get(value->list.ordered, 1); struct byte_array *file_bytes = read_file(path->str, 0, 0); if (NULL == file_bytes) return variable_new_nil(context); struct variable *v = variable_deserialize(context, file_bytes); byte_array_del(file_bytes); return v; }
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 struct variable *get_value(struct context *context, enum Opcode op) { bool interim = op == VM_STX || op == VM_PTX; struct variable *value = stack_peek(context->operand_stack, 0); null_check(value); if (value->type == VAR_SRC) { struct array *values = value->list; if (values->length > values->current) value = (struct variable*)array_get(values, values->current++); else value = variable_new_nil(context); if (interim) values->current = 0; } else if (!interim) variable_pop(context); return value; }
void func_call(struct context *context, enum Opcode op, struct byte_array *program, struct variable *indexable) { struct variable *func = context->runtime ? (struct variable*)variable_pop(context): NULL; struct variable *s = src(context, op, program); if (!context->runtime) return; if (indexable) array_insert(s->list, 0, indexable); // self vm_call_src(context, func); struct variable *result = (struct variable*)stack_peek(context->operand_stack, 0); bool resulted = (result && result->type == VAR_SRC); if (!resulted) { // need a result for an expression, so pretend it returned nil struct variable *v = variable_new_src(context, 0); array_add(v->list, variable_new_nil(context)); stack_push(context->operand_stack, v); } }
// returns contents and modification time of file struct variable *sys_read(struct context *context) { struct variable *args = (struct variable*)stack_pop(context->operand_stack); struct variable *path = param_var(args, 1); uint32_t offset = param_int(args, 2); uint32_t length = param_int(args, 3); struct byte_array *bytes = read_file(path->str, offset, length); DEBUGPRINT("read %d bytes\n", bytes ? bytes->length : 0); struct variable *content = NULL; if (NULL != bytes) { content = variable_new_str(context, bytes); byte_array_del(bytes); } else { context->error = variable_new_str_chars(context, "could not load file"); content = variable_new_nil(context); } return content; }
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; } }
static void push_nil(struct context *context) { struct variable* var = variable_new_nil(context); VM_DEBUGPRINT("NIL\n"); variable_push(context, var); }
struct variable *builtin_method(struct context *context, struct variable *indexable, const struct variable *index) { enum VarType it = indexable->type; char *idxstr = byte_array_to_string(index->str); struct variable *result = NULL; if (!strcmp(idxstr, FNC_LENGTH)) { int n; switch (indexable->type) { case VAR_LST: n = indexable->list.ordered->length; break; case VAR_STR: n = indexable->str->length; break; case VAR_NIL: n = 0; break; default: free(idxstr); exit_message("no length for non-indexable"); return NULL; } result = variable_new_int(context, n); } else if (!strcmp(idxstr, FNC_TYPE)) { const char *typestr = var_type_str(it); result = variable_new_str_chars(context, typestr); } else if (!strcmp(idxstr, FNC_STRING)) { switch (indexable->type) { case VAR_STR: case VAR_BYT: case VAR_FNC: result = variable_copy(context, indexable); break; default: { struct byte_array *vv = variable_value(context, indexable); result = variable_new_str(context, vv); byte_array_del(vv); break; } } } else if (!strcmp(idxstr, FNC_LIST)) result = variable_new_list(context, indexable->list.ordered); else if (!strcmp(idxstr, FNC_KEY)) { if (indexable->type == VAR_KVP) result = indexable->kvp.key; else result = variable_new_nil(context); } else if (!strcmp(idxstr, FNC_VAL)) { if (indexable->type == VAR_KVP) result = indexable->kvp.val; else result = variable_new_nil(context); } else if (!strcmp(idxstr, FNC_KEYS)) result = variable_map_list(context, indexable, &map_keys); else if (!strcmp(idxstr, FNC_VALS)) result = variable_map_list(context, indexable, &map_vals); else if (!strcmp(idxstr, FNC_PACK)) result = variable_new_cfnc(context, &cfnc_pack); else if (!strcmp(idxstr, FNC_SERIALIZE)) result = variable_new_cfnc(context, &cfnc_serialize); else if (!strcmp(idxstr, FNC_DESERIALIZE)) result = variable_new_cfnc(context, &cfnc_deserialize); else if (!strcmp(idxstr, FNC_SORT)) { assert_message(indexable->type == VAR_LST, "sorting non-list"); result = variable_new_cfnc(context, &cfnc_sort); } else if (!strcmp(idxstr, FNC_CHAR)) result = variable_new_cfnc(context, &cfnc_char); else if (!strcmp(idxstr, FNC_HAS)) result = variable_new_cfnc(context, &cfnc_has); else if (!strcmp(idxstr, FNC_FIND)) result = variable_new_cfnc(context, &cfnc_find); else if (!strcmp(idxstr, FNC_PART)) result = variable_new_cfnc(context, &cfnc_part); else if (!strcmp(idxstr, FNC_REMOVE)) result = variable_new_cfnc(context, &cfnc_remove); else if (!strcmp(idxstr, FNC_INSERT)) result = variable_new_cfnc(context, &cfnc_insert); else if (!strcmp(idxstr, FNC_REPLACE)) result = variable_new_cfnc(context, &cfnc_replace); free(idxstr); return result; }