@@ -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); | |||
} | |||
} |