l2_vm_print_state(vm); | l2_vm_print_state(vm); | ||||
char buf[16]; | char buf[16]; | ||||
while ((enum l2_opcode)vm->ops[vm->iptr] != L2_OP_HALT) { | |||||
while (!vm->halted) { | |||||
size_t iptr = vm->iptr; | size_t iptr = vm->iptr; | ||||
printf("\n======\n\n(%d) Will run instr: ", vm->iptr); | printf("\n======\n\n(%d) Will run instr: ", vm->iptr); | ||||
l2_vm_print_op(vm->ops, vm->opcount, &iptr); | l2_vm_print_op(vm->ops, vm->opcount, &iptr); |
* Discard the top element from the stack. | * Discard the top element from the stack. | ||||
* Pop <word> | * Pop <word> | ||||
*/ | */ | ||||
L2_OP_POP, | |||||
L2_OP_DISCARD, | |||||
/* | /* | ||||
* Swap the top and second-top elements, then pop the new top element. | * Swap the top and second-top elements, then pop the new top element. | ||||
* Pop <word2> | * Pop <word2> | ||||
* Push <word1> | * Push <word1> | ||||
*/ | */ | ||||
L2_OP_SWAP_POP, | |||||
L2_OP_SWAP_DISCARD, | |||||
/* | /* | ||||
* Duplicate the top element on the stack. | * Duplicate the top element on the stack. |
void l2_gen_halt(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_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_ret(struct l2_generator *gen); | ||||
void l2_gen_none(struct l2_generator *gen); | void l2_gen_none(struct l2_generator *gen); | ||||
void l2_gen_number(struct l2_generator *gen, double num); | void l2_gen_number(struct l2_generator *gen, double num); |
L2_VAL_TYPE_ERROR, | L2_VAL_TYPE_ERROR, | ||||
}; | }; | ||||
const char *l2_value_type_name(enum l2_value_type typ); | |||||
enum l2_value_flags { | enum l2_value_flags { | ||||
L2_VAL_MARKED = 1 << 6, | L2_VAL_MARKED = 1 << 6, | ||||
L2_VAL_CONST = 1 << 7, | L2_VAL_CONST = 1 << 7, | ||||
int l2_vm_namespace_replace(struct l2_vm *vm, 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 { | struct l2_vm { | ||||
int halted; | |||||
l2_word *ops; | l2_word *ops; | ||||
size_t opcount; | size_t opcount; | ||||
l2_word iptr; | l2_word iptr; | ||||
void l2_vm_init(struct l2_vm *vm, l2_word *ops, size_t opcount); | 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_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_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_free(struct l2_vm *vm); | ||||
void l2_vm_step(struct l2_vm *vm); | void l2_vm_step(struct l2_vm *vm); | ||||
void l2_vm_run(struct l2_vm *vm); | void l2_vm_run(struct l2_vm *vm); |
put(gen, 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) { | void l2_gen_ret(struct l2_generator *gen) { |
} | } | ||||
l2_gen_namespace_set(gen, &key); | l2_gen_namespace_set(gen, &key); | ||||
l2_gen_pop(gen); | |||||
l2_gen_discard(gen); | |||||
tok = l2_lexer_peek(lexer, 1); | tok = l2_lexer_peek(lexer, 1); | ||||
if (tok->kind != L2_TOK_EOL && tok->kind != L2_TOK_CLOSE_BRACE) { | if (tok->kind != L2_TOK_EOL && tok->kind != L2_TOK_CLOSE_BRACE) { | ||||
} | } | ||||
if (!first) { | if (!first) { | ||||
l2_gen_pop(gen); | |||||
l2_gen_discard(gen); | |||||
} | } | ||||
l2_trace_scope("function literal expression"); | l2_trace_scope("function literal expression"); | ||||
} | } | ||||
l2_gen_namespace_set(gen, &ident); | 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) { | } else if (tok->kind == L2_TOK_PERIOD && tok2->kind == L2_TOK_IDENT) { | ||||
l2_trace_scope("namespace lookup"); | l2_trace_scope("namespace lookup"); | ||||
l2_trace("ident '%s'", tok2->v.str); | l2_trace("ident '%s'", tok2->v.str); | ||||
} | } | ||||
l2_gen_array_set(gen, number); | l2_gen_array_set(gen, number); | ||||
l2_gen_swap_pop(gen); | |||||
l2_gen_swap_discard(gen); | |||||
} else if (tok->kind == L2_TOK_DOT_NUMBER) { | } else if (tok->kind == L2_TOK_DOT_NUMBER) { | ||||
l2_trace_scope("direct array lookup"); | l2_trace_scope("direct array lookup"); | ||||
int number = tok->v.integer; | int number = tok->v.integer; | ||||
return -1; | return -1; | ||||
} | } | ||||
l2_gen_pop(gen); | |||||
l2_gen_discard(gen); | |||||
} | } | ||||
l2_gen_halt(gen); | l2_gen_halt(gen); |
for (size_t i = 0; i < args->len; ++i) { | for (size_t i = 0; i < args->len; ++i) { | ||||
struct l2_vm_value *val = &vm->values[args->data[i]]; | struct l2_vm_value *val = &vm->values[args->data[i]]; | ||||
if (l2_vm_value_type(val) != L2_VAL_TYPE_REAL) { | if (l2_vm_value_type(val) != L2_VAL_TYPE_REAL) { | ||||
// TODO: Error | |||||
return l2_vm_type_error(vm, val); | |||||
} | } | ||||
sum += val->real; | sum += val->real; |
printf("NOP\n"); | printf("NOP\n"); | ||||
return; | return; | ||||
case L2_OP_POP: | |||||
printf("POP\n"); | |||||
case L2_OP_DISCARD: | |||||
printf("DISCARD\n"); | |||||
return; | return; | ||||
case L2_OP_SWAP_POP: | |||||
printf("SWAP_POP\n"); | |||||
case L2_OP_SWAP_DISCARD: | |||||
printf("SWAP_DISCARD\n"); | |||||
return; | return; | ||||
case L2_OP_DUP: | case L2_OP_DUP: |
return freed; | 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) { | void l2_vm_init(struct l2_vm *vm, l2_word *ops, size_t opcount) { | ||||
if (!stdio_inited) { | if (!stdio_inited) { | ||||
std_output.w.write = l2_io_file_write; | std_output.w.write = l2_io_file_write; | ||||
vm->std_output = &std_output.w; | vm->std_output = &std_output.w; | ||||
vm->std_error = &std_error.w; | vm->std_error = &std_error.w; | ||||
vm->halted = 0; | |||||
vm->ops = ops; | vm->ops = ops; | ||||
vm->opcount = opcount; | vm->opcount = opcount; | ||||
vm->iptr = 0; | vm->iptr = 0; | ||||
return id; | 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) { | void l2_vm_free(struct l2_vm *vm) { | ||||
// Skip ID 0, because that's always NONE | // Skip ID 0, because that's always NONE | ||||
for (size_t i = 1; i < vm->valuessize; ++i) { | for (size_t i = 1; i < vm->valuessize; ++i) { | ||||
} | } | ||||
void l2_vm_run(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); | l2_vm_step(vm); | ||||
} | } | ||||
} | } | ||||
case L2_OP_NOP: | case L2_OP_NOP: | ||||
break; | break; | ||||
case L2_OP_POP: | |||||
case L2_OP_DISCARD: | |||||
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; | break; | ||||
case L2_OP_SWAP_POP: | |||||
case L2_OP_SWAP_DISCARD: | |||||
vm->stack[vm->sptr - 2] = vm->stack[vm->sptr - 1]; | vm->stack[vm->sptr - 2] = vm->stack[vm->sptr - 1]; | ||||
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; | break; | ||||
case L2_OP_DUP: | case L2_OP_DUP: | ||||
break; | break; | ||||
case L2_OP_HALT: | case L2_OP_HALT: | ||||
vm->halted = 1; | |||||
break; | break; | ||||
} | } | ||||
} | } |
vm.std_output = &output.w; | vm.std_output = &output.w; | ||||
// Run a GC after every instruction to uncover potential GC issues | // 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_step(&vm); | ||||
l2_vm_gc(&vm); | l2_vm_gc(&vm); | ||||
} | } |