*/ | */ | ||||
L2_OP_CALL, | L2_OP_CALL, | ||||
/* | |||||
* Jump relative. | |||||
* Pop <word> | |||||
* Jump <word> words forwards | |||||
*/ | |||||
L2_OP_RJMP, | |||||
/* | /* | ||||
* Generate a stack frame. | * Generate a stack frame. | ||||
* Alloc namespace <var> | * Alloc namespace <var> |
#include "../io.h" | #include "../io.h" | ||||
#include "../strset.h" | #include "../strset.h" | ||||
#include "../bytecode.h" | |||||
struct l2_generator_string { | |||||
l2_word length; | |||||
l2_word pos; | |||||
}; | |||||
struct l2_generator { | 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; | struct l2_bufio_writer writer; | ||||
}; | }; | ||||
void l2_gen_stack_frame(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_assignment(struct l2_generator *gen, char **ident); | ||||
void l2_gen_number(struct l2_generator *gen, double num); | 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); | void l2_gen_namespace_lookup(struct l2_generator *gen, char **ident); | ||||
#endif | #endif |
static void put(struct l2_generator *gen, l2_word word) { | static void put(struct l2_generator *gen, l2_word word) { | ||||
l2_bufio_put_n(&gen->writer, &word, sizeof(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) { | 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); | l2_bufio_writer_init(&gen->writer, w); | ||||
} | } | ||||
} | } | ||||
void l2_gen_free(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: | // Postconditions: | ||||
// * The namespace contains the new value under key 'ident' | // * The namespace contains the new value under key 'ident' | ||||
// * Stack(0) is untouched | // * Stack(0) is untouched | ||||
void l2_gen_assignment(struct l2_generator *gen, char **ident) { | 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, L2_OP_PUSH); | ||||
put(gen, atom_id); | put(gen, atom_id); | ||||
put(gen, L2_OP_STACK_FRAME_SET); | put(gen, L2_OP_STACK_FRAME_SET); | ||||
} | } | ||||
// Postconditions; | // Postconditions; | ||||
// * Stack(0) is changed to a number value | // * Stack(0) is changed to a number value | ||||
void l2_gen_number(struct l2_generator *gen, double num) { | void l2_gen_number(struct l2_generator *gen, double num) { | ||||
put(gen, L2_OP_ALLOC_REAL_64); | 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: | // Postconditions: | ||||
// * Stack(0) is any value | // * Stack(0) is any value | ||||
void l2_gen_namespace_lookup(struct l2_generator *gen, char **ident) { | 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, L2_OP_PUSH); | ||||
put(gen, atom_id); | put(gen, atom_id); | ||||
put(gen, L2_OP_STACK_FRAME_LOOKUP); | put(gen, L2_OP_STACK_FRAME_LOOKUP); |
l2_lexer_consume(lexer); // ident | l2_lexer_consume(lexer); // ident | ||||
l2_gen_namespace_lookup(gen, &ident); | l2_gen_namespace_lookup(gen, &ident); | ||||
return 0; | 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", | l2_parse_err(err, tok, "In expression: Unexpected tokens %s, %s", |
static struct l2_generator gen; | static struct l2_generator gen; | ||||
static struct l2_io_mem_writer w; | static struct l2_io_mem_writer w; | ||||
static struct l2_vm vm; | static struct l2_vm vm; | ||||
static struct l2_parse_error err; | |||||
static struct l2_vm_value *var_lookup(const char *name) { | 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); | l2_word id = l2_vm_namespace_get(&vm.values[vm.nstack[0]], atom_id); | ||||
return &vm.values[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.r.read = l2_io_mem_read; | ||||
r.idx = 0; | r.idx = 0; | ||||
r.len = strlen(str); | r.len = strlen(str); | ||||
w.mem = NULL; | w.mem = NULL; | ||||
l2_gen_init(&gen, (struct l2_io_writer *)&w); | 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); | free(w.mem); | ||||
return -1; | return -1; | ||||
} | } | ||||
return 0; | 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) { | describe(eval) { | ||||
test("eval assignment") { | |||||
test("assignment") { | |||||
eval("foo := 10"); | eval("foo := 10"); | ||||
defer(l2_vm_free(&vm)); | defer(l2_vm_free(&vm)); | ||||
defer(l2_gen_free(&gen)); | 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"); | eval("foo := 10\nbar := foo"); | ||||
defer(l2_vm_free(&vm)); | defer(l2_vm_free(&vm)); | ||||
defer(l2_gen_free(&gen)); | 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); | |||||
} | } | ||||
} | } |