@@ -13,7 +13,7 @@ void step_through(struct l2_vm *vm) { | |||
l2_vm_print_state(vm); | |||
char buf[16]; | |||
while ((enum l2_opcode)vm->ops[vm->iptr] != L2_OP_HALT) { | |||
while (!vm->halted) { | |||
size_t iptr = vm->iptr; | |||
printf("\n======\n\n(%d) Will run instr: ", vm->iptr); | |||
l2_vm_print_op(vm->ops, vm->opcount, &iptr); |
@@ -15,7 +15,7 @@ enum l2_opcode { | |||
* Discard the top element from the stack. | |||
* Pop <word> | |||
*/ | |||
L2_OP_POP, | |||
L2_OP_DISCARD, | |||
/* | |||
* Swap the top and second-top elements, then pop the new top element. | |||
@@ -23,7 +23,7 @@ enum l2_opcode { | |||
* Pop <word2> | |||
* Push <word1> | |||
*/ | |||
L2_OP_SWAP_POP, | |||
L2_OP_SWAP_DISCARD, | |||
/* | |||
* Duplicate the top element on the stack. |
@@ -24,8 +24,8 @@ void l2_gen_free(struct l2_generator *gen); | |||
void l2_gen_halt(struct l2_generator *gen); | |||
void l2_gen_rjmp(struct l2_generator *gen, l2_word len); | |||
void l2_gen_pop(struct l2_generator *gen); | |||
void l2_gen_swap_pop(struct l2_generator *gen); | |||
void l2_gen_discard(struct l2_generator *gen); | |||
void l2_gen_swap_discard(struct l2_generator *gen); | |||
void l2_gen_ret(struct l2_generator *gen); | |||
void l2_gen_none(struct l2_generator *gen); | |||
void l2_gen_number(struct l2_generator *gen, double num); |
@@ -23,6 +23,8 @@ enum l2_value_type { | |||
L2_VAL_TYPE_ERROR, | |||
}; | |||
const char *l2_value_type_name(enum l2_value_type typ); | |||
enum l2_value_flags { | |||
L2_VAL_MARKED = 1 << 6, | |||
L2_VAL_CONST = 1 << 7, | |||
@@ -88,6 +90,7 @@ void l2_vm_namespace_set(struct l2_vm_value *ns, l2_word key, l2_word val); | |||
int l2_vm_namespace_replace(struct l2_vm *vm, struct l2_vm_value *ns, l2_word key, l2_word val); | |||
struct l2_vm { | |||
int halted; | |||
l2_word *ops; | |||
size_t opcount; | |||
l2_word iptr; | |||
@@ -109,6 +112,7 @@ struct l2_vm { | |||
void l2_vm_init(struct l2_vm *vm, l2_word *ops, size_t opcount); | |||
l2_word l2_vm_alloc(struct l2_vm *vm, enum l2_value_type typ, enum l2_value_flags flags); | |||
l2_word l2_vm_error(struct l2_vm *vm, const char *fmt, ...); | |||
l2_word l2_vm_type_error(struct l2_vm *vm, struct l2_vm_value *val); | |||
void l2_vm_free(struct l2_vm *vm); | |||
void l2_vm_step(struct l2_vm *vm); | |||
void l2_vm_run(struct l2_vm *vm); |
@@ -40,12 +40,12 @@ void l2_gen_rjmp(struct l2_generator *gen, l2_word len) { | |||
put(gen, len); | |||
} | |||
void l2_gen_pop(struct l2_generator *gen) { | |||
put(gen, L2_OP_POP); | |||
void l2_gen_discard(struct l2_generator *gen) { | |||
put(gen, L2_OP_DISCARD); | |||
} | |||
void l2_gen_swap_pop(struct l2_generator *gen) { | |||
put(gen, L2_OP_SWAP_POP); | |||
void l2_gen_swap_discard(struct l2_generator *gen) { | |||
put(gen, L2_OP_SWAP_DISCARD); | |||
} | |||
void l2_gen_ret(struct l2_generator *gen) { |
@@ -52,7 +52,7 @@ static int parse_object_literal( | |||
} | |||
l2_gen_namespace_set(gen, &key); | |||
l2_gen_pop(gen); | |||
l2_gen_discard(gen); | |||
tok = l2_lexer_peek(lexer, 1); | |||
if (tok->kind != L2_TOK_EOL && tok->kind != L2_TOK_CLOSE_BRACE) { | |||
@@ -87,7 +87,7 @@ static int parse_function_literal_impl( | |||
} | |||
if (!first) { | |||
l2_gen_pop(gen); | |||
l2_gen_discard(gen); | |||
} | |||
l2_trace_scope("function literal expression"); | |||
@@ -300,7 +300,7 @@ static int parse_arg_level_expression( | |||
} | |||
l2_gen_namespace_set(gen, &ident); | |||
l2_gen_swap_pop(gen); | |||
l2_gen_swap_discard(gen); | |||
} else if (tok->kind == L2_TOK_PERIOD && tok2->kind == L2_TOK_IDENT) { | |||
l2_trace_scope("namespace lookup"); | |||
l2_trace("ident '%s'", tok2->v.str); | |||
@@ -320,7 +320,7 @@ static int parse_arg_level_expression( | |||
} | |||
l2_gen_array_set(gen, number); | |||
l2_gen_swap_pop(gen); | |||
l2_gen_swap_discard(gen); | |||
} else if (tok->kind == L2_TOK_DOT_NUMBER) { | |||
l2_trace_scope("direct array lookup"); | |||
int number = tok->v.integer; | |||
@@ -441,7 +441,7 @@ int l2_parse_program( | |||
return -1; | |||
} | |||
l2_gen_pop(gen); | |||
l2_gen_discard(gen); | |||
} | |||
l2_gen_halt(gen); |
@@ -59,7 +59,7 @@ l2_word l2_builtin_add(struct l2_vm *vm, struct l2_vm_array *args) { | |||
for (size_t i = 0; i < args->len; ++i) { | |||
struct l2_vm_value *val = &vm->values[args->data[i]]; | |||
if (l2_vm_value_type(val) != L2_VAL_TYPE_REAL) { | |||
// TODO: Error | |||
return l2_vm_type_error(vm, val); | |||
} | |||
sum += val->real; |
@@ -118,12 +118,12 @@ void l2_vm_print_op(l2_word *ops, size_t opcount, size_t *ptr) { | |||
printf("NOP\n"); | |||
return; | |||
case L2_OP_POP: | |||
printf("POP\n"); | |||
case L2_OP_DISCARD: | |||
printf("DISCARD\n"); | |||
return; | |||
case L2_OP_SWAP_POP: | |||
printf("SWAP_POP\n"); | |||
case L2_OP_SWAP_DISCARD: | |||
printf("SWAP_DISCARD\n"); | |||
return; | |||
case L2_OP_DUP: |
@@ -121,6 +121,22 @@ static size_t gc_sweep(struct l2_vm *vm) { | |||
return freed; | |||
} | |||
const char *l2_value_type_name(enum l2_value_type typ) { | |||
switch (typ) { | |||
case L2_VAL_TYPE_NONE: return "NONE"; | |||
case L2_VAL_TYPE_ATOM: return "ATOM"; | |||
case L2_VAL_TYPE_REAL: return "REAL"; | |||
case L2_VAL_TYPE_BUFFER: return "BUFFER"; | |||
case L2_VAL_TYPE_ARRAY: return "ARRAY"; | |||
case L2_VAL_TYPE_NAMESPACE: return "NAMESPACE"; | |||
case L2_VAL_TYPE_FUNCTION: return "FUNCTION"; | |||
case L2_VAL_TYPE_CFUNCTION: return "CFUNCTION"; | |||
case L2_VAL_TYPE_ERROR: return "ERROR"; | |||
} | |||
return "(unknown)"; | |||
} | |||
void l2_vm_init(struct l2_vm *vm, l2_word *ops, size_t opcount) { | |||
if (!stdio_inited) { | |||
std_output.w.write = l2_io_file_write; | |||
@@ -133,6 +149,7 @@ void l2_vm_init(struct l2_vm *vm, l2_word *ops, size_t opcount) { | |||
vm->std_output = &std_output.w; | |||
vm->std_error = &std_error.w; | |||
vm->halted = 0; | |||
vm->ops = ops; | |||
vm->opcount = opcount; | |||
vm->iptr = 0; | |||
@@ -215,6 +232,10 @@ l2_word l2_vm_error(struct l2_vm *vm, const char *fmt, ...) { | |||
return id; | |||
} | |||
l2_word l2_vm_type_error(struct l2_vm *vm, struct l2_vm_value *val) { | |||
return l2_vm_error(vm, "Unexpected type %s", l2_value_type_name(l2_vm_value_type(val))); | |||
} | |||
void l2_vm_free(struct l2_vm *vm) { | |||
// Skip ID 0, because that's always NONE | |||
for (size_t i = 1; i < vm->valuessize; ++i) { | |||
@@ -242,7 +263,7 @@ size_t l2_vm_gc(struct l2_vm *vm) { | |||
} | |||
void l2_vm_run(struct l2_vm *vm) { | |||
while ((enum l2_opcode)vm->ops[vm->iptr] != L2_OP_HALT) { | |||
while (!vm->halted) { | |||
l2_vm_step(vm); | |||
} | |||
} | |||
@@ -255,13 +276,21 @@ void l2_vm_step(struct l2_vm *vm) { | |||
case L2_OP_NOP: | |||
break; | |||
case L2_OP_POP: | |||
case L2_OP_DISCARD: | |||
vm->sptr -= 1; | |||
if (l2_vm_value_type(&vm->values[vm->stack[vm->sptr]]) == L2_VAL_TYPE_ERROR) { | |||
l2_io_printf(vm->std_error, "Error: %s\n", vm->values[vm->stack[vm->sptr]].error); | |||
vm->halted = 1; | |||
} | |||
break; | |||
case L2_OP_SWAP_POP: | |||
case L2_OP_SWAP_DISCARD: | |||
vm->stack[vm->sptr - 2] = vm->stack[vm->sptr - 1]; | |||
vm->sptr -= 1; | |||
if (l2_vm_value_type(&vm->values[vm->stack[vm->sptr]]) == L2_VAL_TYPE_ERROR) { | |||
l2_io_printf(vm->std_error, "Error: %s\n", vm->values[vm->stack[vm->sptr]].error); | |||
vm->halted = 1; | |||
} | |||
break; | |||
case L2_OP_DUP: | |||
@@ -533,6 +562,7 @@ void l2_vm_step(struct l2_vm *vm) { | |||
break; | |||
case L2_OP_HALT: | |||
vm->halted = 1; | |||
break; | |||
} | |||
} |
@@ -111,7 +111,7 @@ static void check_impl(const char *name) { | |||
vm.std_output = &output.w; | |||
// Run a GC after every instruction to uncover potential GC issues | |||
while (vm.ops[vm.iptr] != L2_OP_HALT) { | |||
while (!vm->halted) { | |||
l2_vm_step(&vm); | |||
l2_vm_gc(&vm); | |||
} |