| @@ -52,6 +52,13 @@ enum l2_opcode { | |||
| */ | |||
| L2_OP_CALL, | |||
| /* | |||
| * Jump relative. | |||
| * Pop <word> | |||
| * Jump <word> words forwards | |||
| */ | |||
| L2_OP_RJMP, | |||
| /* | |||
| * Generate a stack frame. | |||
| * Alloc namespace <var> | |||
| @@ -3,10 +3,18 @@ | |||
| #include "../io.h" | |||
| #include "../strset.h" | |||
| #include "../bytecode.h" | |||
| struct l2_generator_string { | |||
| l2_word length; | |||
| l2_word pos; | |||
| }; | |||
| struct l2_generator { | |||
| struct l2_strset atoms; | |||
| struct l2_strset strings; | |||
| struct l2_strset atomset; | |||
| struct l2_strset stringset; | |||
| struct l2_generator_string *strings; | |||
| l2_word pos; | |||
| struct l2_bufio_writer writer; | |||
| }; | |||
| @@ -18,6 +26,7 @@ void l2_gen_halt(struct l2_generator *gen); | |||
| void l2_gen_stack_frame(struct l2_generator *gen); | |||
| void l2_gen_assignment(struct l2_generator *gen, char **ident); | |||
| void l2_gen_number(struct l2_generator *gen, double num); | |||
| void l2_gen_string(struct l2_generator *gen, char **str); | |||
| void l2_gen_namespace_lookup(struct l2_generator *gen, char **ident); | |||
| #endif | |||
| @@ -4,11 +4,14 @@ | |||
| static void put(struct l2_generator *gen, l2_word word) { | |||
| l2_bufio_put_n(&gen->writer, &word, sizeof(word)); | |||
| gen->pos += 1; | |||
| } | |||
| void l2_gen_init(struct l2_generator *gen, struct l2_io_writer *w) { | |||
| l2_strset_init(&gen->atoms); | |||
| l2_strset_init(&gen->strings); | |||
| l2_strset_init(&gen->atomset); | |||
| l2_strset_init(&gen->stringset); | |||
| gen->strings = NULL; | |||
| gen->pos = 0; | |||
| l2_bufio_writer_init(&gen->writer, w); | |||
| } | |||
| @@ -17,8 +20,8 @@ void l2_gen_flush(struct l2_generator *gen) { | |||
| } | |||
| void l2_gen_free(struct l2_generator *gen) { | |||
| l2_strset_free(&gen->atoms); | |||
| l2_strset_free(&gen->strings); | |||
| l2_strset_free(&gen->atomset); | |||
| l2_strset_free(&gen->stringset); | |||
| } | |||
| // Postconditions: | |||
| @@ -39,13 +42,12 @@ void l2_gen_stack_frame(struct l2_generator *gen) { | |||
| // * The namespace contains the new value under key 'ident' | |||
| // * Stack(0) is untouched | |||
| void l2_gen_assignment(struct l2_generator *gen, char **ident) { | |||
| size_t atom_id = l2_strset_put(&gen->atoms, ident); | |||
| size_t atom_id = l2_strset_put(&gen->atomset, ident); | |||
| put(gen, L2_OP_PUSH); | |||
| put(gen, atom_id); | |||
| put(gen, L2_OP_STACK_FRAME_SET); | |||
| } | |||
| // Postconditions; | |||
| // * Stack(0) is changed to a number value | |||
| void l2_gen_number(struct l2_generator *gen, double num) { | |||
| @@ -57,10 +59,46 @@ void l2_gen_number(struct l2_generator *gen, double num) { | |||
| put(gen, L2_OP_ALLOC_REAL_64); | |||
| } | |||
| void l2_gen_string(struct l2_generator *gen, char **str) { | |||
| size_t id = l2_strset_get(&gen->stringset, *str); | |||
| if (id == 0) { | |||
| size_t len = strlen(*str); | |||
| size_t aligned = len; | |||
| if (aligned % sizeof(l2_word) != 0) { | |||
| aligned += sizeof(l2_word) - (aligned % sizeof(l2_word)); | |||
| } | |||
| put(gen, L2_OP_PUSH); | |||
| put(gen, aligned / sizeof(l2_word)); | |||
| l2_word pos = gen->pos; | |||
| l2_bufio_put_n(&gen->writer, *str, len); | |||
| for (size_t i = len; i < aligned; ++i) { | |||
| l2_bufio_put(&gen->writer, '\0'); | |||
| } | |||
| id = l2_strset_put(&gen->stringset, str); | |||
| gen->strings = realloc(gen->strings, id * sizeof(*gen->strings)); | |||
| gen->strings[id - 1].length = len; | |||
| gen->strings[id - 1].pos = pos; | |||
| put(gen, L2_OP_PUSH_2); | |||
| put(gen, pos); | |||
| put(gen, len); | |||
| put(gen, L2_OP_ALLOC_BUFFER_STATIC); | |||
| } else { | |||
| struct l2_generator_string *s = &gen->strings[id - 1]; | |||
| put(gen, L2_OP_PUSH_2); | |||
| put(gen, s->pos); | |||
| put(gen, s->length); | |||
| put(gen, L2_OP_ALLOC_BUFFER_STATIC); | |||
| } | |||
| } | |||
| // Postconditions: | |||
| // * Stack(0) is any value | |||
| void l2_gen_namespace_lookup(struct l2_generator *gen, char **ident) { | |||
| size_t atom_id = l2_strset_put(&gen->atoms, ident); | |||
| size_t atom_id = l2_strset_put(&gen->atomset, ident); | |||
| put(gen, L2_OP_PUSH); | |||
| put(gen, atom_id); | |||
| put(gen, L2_OP_STACK_FRAME_LOOKUP); | |||
| @@ -28,6 +28,11 @@ static int parse_expression( | |||
| l2_lexer_consume(lexer); // ident | |||
| l2_gen_namespace_lookup(gen, &ident); | |||
| return 0; | |||
| } else if (tok->kind == L2_TOK_STRING) { | |||
| char *str = l2_token_extract_str(tok); | |||
| l2_lexer_consume(lexer); // string | |||
| l2_gen_string(gen, &str); | |||
| return 0; | |||
| } | |||
| l2_parse_err(err, tok, "In expression: Unexpected tokens %s, %s", | |||
| @@ -10,15 +10,14 @@ static struct l2_io_mem_reader r; | |||
| static struct l2_generator gen; | |||
| static struct l2_io_mem_writer w; | |||
| static struct l2_vm vm; | |||
| static struct l2_parse_error err; | |||
| static struct l2_vm_value *var_lookup(const char *name) { | |||
| l2_word atom_id = l2_strset_get(&gen.atoms, name); | |||
| l2_word atom_id = l2_strset_get(&gen.atomset, name); | |||
| l2_word id = l2_vm_namespace_get(&vm.values[vm.nstack[0]], atom_id); | |||
| return &vm.values[id]; | |||
| } | |||
| static int eval(const char *str) { | |||
| static int eval_impl(const char *str, struct l2_parse_error *err) { | |||
| r.r.read = l2_io_mem_read; | |||
| r.idx = 0; | |||
| r.len = strlen(str); | |||
| @@ -30,7 +29,7 @@ static int eval(const char *str) { | |||
| w.mem = NULL; | |||
| l2_gen_init(&gen, (struct l2_io_writer *)&w); | |||
| if (l2_parse_program(&lex, &gen, &err) < 0) { | |||
| if (l2_parse_program(&lex, &gen, err) < 0) { | |||
| free(w.mem); | |||
| return -1; | |||
| } | |||
| @@ -42,24 +41,43 @@ static int eval(const char *str) { | |||
| return 0; | |||
| } | |||
| #define eval(str) do { \ | |||
| snow_fail_update(); \ | |||
| struct l2_parse_error err; \ | |||
| if (eval_impl(str, &err) < 0) { \ | |||
| snow_fail("Parsing failed: %i:%i: %s", err.line, err.ch, err.message); \ | |||
| } \ | |||
| } while (0) | |||
| describe(eval) { | |||
| test("eval assignment") { | |||
| test("assignment") { | |||
| eval("foo := 10"); | |||
| defer(l2_vm_free(&vm)); | |||
| defer(l2_gen_free(&gen)); | |||
| assert(l2_vm_value_type(var_lookup("foo")) == L2_VAL_TYPE_REAL); | |||
| assert(var_lookup("foo")->real == 10); | |||
| asserteq(l2_vm_value_type(var_lookup("foo")), L2_VAL_TYPE_REAL); | |||
| asserteq(var_lookup("foo")->real, 10); | |||
| } | |||
| test("eval var deref assignment") { | |||
| test("var deref assignment") { | |||
| eval("foo := 10\nbar := foo"); | |||
| defer(l2_vm_free(&vm)); | |||
| defer(l2_gen_free(&gen)); | |||
| assert(l2_vm_value_type(var_lookup("foo")) == L2_VAL_TYPE_REAL); | |||
| assert(var_lookup("foo")->real == 10); | |||
| assert(l2_vm_value_type(var_lookup("bar")) == L2_VAL_TYPE_REAL); | |||
| assert(var_lookup("bar")->real == 10); | |||
| asserteq(l2_vm_value_type(var_lookup("foo")), L2_VAL_TYPE_REAL); | |||
| asserteq(var_lookup("foo")->real, 10); | |||
| asserteq(l2_vm_value_type(var_lookup("bar")), L2_VAL_TYPE_REAL); | |||
| asserteq(var_lookup("bar")->real, 10); | |||
| } | |||
| test("string assignment") { | |||
| eval("foo := \"hello world\""); | |||
| defer(l2_vm_free(&vm)); | |||
| defer(l2_gen_free(&gen)); | |||
| asserteq(l2_vm_value_type(var_lookup("foo")), L2_VAL_TYPE_BUFFER); | |||
| struct l2_vm_buffer *buf = var_lookup("foo")->data; | |||
| asserteq(buf->len, 11); | |||
| assert(strncmp(buf->data, "hello world", 11) == 0); | |||
| } | |||
| } | |||