@@ -1,42 +1,53 @@ | |||
#include "vm/vm.h" | |||
#include "vm/print.h" | |||
#include "parse/parse.h" | |||
#include "parse/lex.h" | |||
#include "io.h" | |||
#include "bitset.h" | |||
#include <stdio.h> | |||
#include <string.h> | |||
int main() { | |||
l2_word ops[] = { | |||
L2_OP_PUSH, 100, | |||
L2_OP_PUSH, 100, | |||
L2_OP_ADD, | |||
L2_OP_ALLOC_INTEGER_32, | |||
L2_OP_PUSH, 21 /* offset */, | |||
L2_OP_PUSH, 5 /* length */, | |||
L2_OP_ALLOC_BUFFER_STATIC, | |||
L2_OP_POP, | |||
L2_OP_PUSH, 16, | |||
L2_OP_CALL, | |||
L2_OP_HALT, | |||
L2_OP_PUSH, 53, | |||
L2_OP_ALLOC_INTEGER_32, | |||
L2_OP_ALLOC_NAMESPACE, | |||
L2_OP_HALT, | |||
0, 0, | |||
}; | |||
memcpy(&ops[21], "Hello", 5); | |||
int main(int argc, char **argv) { | |||
if (argc != 1 && argc != 2) { | |||
fprintf(stderr, "Usage: %s [file]\n", argv[0]); | |||
return 1; | |||
} | |||
struct l2_vm vm; | |||
l2_vm_init(&vm, ops, sizeof(ops) / sizeof(*ops)); | |||
FILE *inf; | |||
if (argc == 1 || (argc == 2 && strcmp(argv[1], "-") == 0)) { | |||
inf = stdin; | |||
} else { | |||
inf = fopen(argv[1], "r"); | |||
} | |||
l2_vm_run(&vm); | |||
struct l2_io_file_reader r; | |||
r.r.read = l2_io_file_read; | |||
r.f = inf; | |||
l2_vm_print_state(&vm); | |||
struct l2_lexer lexer; | |||
l2_lexer_init(&lexer, &r.r); | |||
struct l2_io_mem_writer w = {0}; | |||
w.w.write = l2_io_mem_write; | |||
struct l2_generator gen; | |||
l2_gen_init(&gen, &w.w); | |||
l2_vm_gc(&vm); | |||
struct l2_parse_error err; | |||
if (l2_parse_program(&lexer, &gen, &err) < 0) { | |||
fprintf(stderr, "Parse error: %s:%i:%i: %s\n", | |||
(argc == 2 ? argv[1] : "-"), err.line, err.ch, err.message); | |||
return 1; | |||
} | |||
printf("Heap:\n"); | |||
l2_vm_print_stack(&vm); | |||
fprintf(stderr, "Generated bytecode:\n"); | |||
l2_vm_print_bytecode((l2_word *)w.mem, w.len / sizeof(l2_word)); | |||
fprintf(stderr, "\n"); | |||
l2_vm_free(&vm); | |||
struct l2_vm vm; | |||
l2_vm_init(&vm, (void *)w.mem, w.len / sizeof(l2_word)); | |||
l2_vm_run(&vm); | |||
l2_vm_print_state(&vm); | |||
} |
@@ -155,6 +155,14 @@ enum l2_opcode { | |||
*/ | |||
L2_OP_ALLOC_NAMESPACE, | |||
/* | |||
* Allocate a function. | |||
* Pop <word> | |||
* Alloc function <var> pointing to location <word> | |||
* Push <var> | |||
*/ | |||
L2_OP_ALLOC_FUNCTION, | |||
/* | |||
* Set a namespace's name to a value. | |||
* Pop <key> |
@@ -24,9 +24,13 @@ void l2_gen_free(struct l2_generator *gen); | |||
void l2_gen_halt(struct l2_generator *gen); | |||
void l2_gen_stack_frame(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_ret(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_function(struct l2_generator *gen, l2_word pos); | |||
void l2_gen_namespace_lookup(struct l2_generator *gen, char **ident); | |||
#endif |
@@ -14,12 +14,17 @@ struct l2_vm_value { | |||
L2_VAL_TYPE_BUFFER, | |||
L2_VAL_TYPE_ARRAY, | |||
L2_VAL_TYPE_NAMESPACE, | |||
L2_VAL_TYPE_FUNCTION, | |||
L2_VAL_MARKED = 1 << 7, | |||
L2_VAL_CONST = 1 << 8, | |||
} flags; | |||
union { | |||
int64_t integer; | |||
double real; | |||
struct { | |||
l2_word pos; | |||
l2_word namespace; | |||
} func; | |||
void *data; | |||
}; | |||
}; |
@@ -24,23 +24,28 @@ void l2_gen_free(struct l2_generator *gen) { | |||
l2_strset_free(&gen->stringset); | |||
} | |||
// Postconditions: | |||
// * Execution is halted | |||
void l2_gen_halt(struct l2_generator *gen) { | |||
put(gen, L2_OP_HALT); | |||
} | |||
// Postconditions: | |||
// * NStack(0) is a namespace value | |||
void l2_gen_stack_frame(struct l2_generator *gen) { | |||
put(gen, L2_OP_GEN_STACK_FRAME); | |||
} | |||
// Preconditions: | |||
// * Stack(0) is any value | |||
// Postconditions: | |||
// * The namespace contains the new value under key 'ident' | |||
// * Stack(0) is untouched | |||
void l2_gen_rjmp(struct l2_generator *gen, l2_word len) { | |||
put(gen, L2_OP_PUSH); | |||
put(gen, len); | |||
put(gen, L2_OP_RJMP); | |||
} | |||
void l2_gen_pop(struct l2_generator *gen) { | |||
put(gen, L2_OP_POP); | |||
} | |||
void l2_gen_ret(struct l2_generator *gen) { | |||
put(gen, L2_OP_RET); | |||
} | |||
void l2_gen_assignment(struct l2_generator *gen, char **ident) { | |||
size_t atom_id = l2_strset_put(&gen->atomset, ident); | |||
put(gen, L2_OP_PUSH); | |||
@@ -48,8 +53,6 @@ void l2_gen_assignment(struct l2_generator *gen, char **ident) { | |||
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) { | |||
uint64_t n; | |||
memcpy(&n, &num, sizeof(num)); | |||
@@ -70,6 +73,7 @@ void l2_gen_string(struct l2_generator *gen, char **str) { | |||
put(gen, L2_OP_PUSH); | |||
put(gen, aligned / sizeof(l2_word)); | |||
put(gen, L2_OP_RJMP); | |||
l2_word pos = gen->pos; | |||
l2_bufio_put_n(&gen->writer, *str, len); | |||
@@ -95,8 +99,12 @@ void l2_gen_string(struct l2_generator *gen, char **str) { | |||
} | |||
} | |||
// Postconditions: | |||
// * Stack(0) is any value | |||
void l2_gen_function(struct l2_generator *gen, l2_word pos) { | |||
put(gen, L2_OP_PUSH); | |||
put(gen, pos); | |||
put(gen, L2_OP_ALLOC_FUNCTION); | |||
} | |||
void l2_gen_namespace_lookup(struct l2_generator *gen, char **ident) { | |||
size_t atom_id = l2_strset_put(&gen->atomset, ident); | |||
put(gen, L2_OP_PUSH); |
@@ -1,7 +1,62 @@ | |||
#include "parse/parse.h" | |||
#include <stdbool.h> | |||
#include "gen/gen.h" | |||
static int parse_expression( | |||
struct l2_lexer *lexer, struct l2_generator *gen, struct l2_parse_error *err); | |||
static int parse_function_impl( | |||
struct l2_lexer *lexer, struct l2_generator *gen, struct l2_parse_error *err) { | |||
l2_gen_stack_frame(gen); | |||
l2_lexer_consume(lexer); // { | |||
l2_lexer_consume(lexer); // } | |||
l2_gen_ret(gen); | |||
return 0; | |||
} | |||
static int parse_function( | |||
struct l2_lexer *lexer, struct l2_generator *gen, struct l2_parse_error *err) { | |||
l2_gen_flush(gen); | |||
struct l2_io_writer *prev_writer = gen->writer.w; | |||
// Generate the function to a buffer in memory | |||
struct l2_io_mem_writer w = {0}; | |||
w.w.write = l2_io_mem_write; | |||
gen->writer.w = &w.w; | |||
// Generates three words; PUSH, 0, RJMP | |||
l2_gen_rjmp(gen, 0); | |||
l2_word pos = gen->pos; | |||
// Generate the function body itself | |||
int ret = parse_function_impl(lexer, gen, err); | |||
l2_gen_flush(gen); | |||
gen->writer.w = prev_writer; | |||
if (ret < 0) { | |||
free(w.mem); | |||
return -1; | |||
} | |||
l2_word *ops = (l2_word *)w.mem; | |||
l2_word opcount = w.len / sizeof(l2_word); | |||
// Due to the earlier gen_rjmp, the second word will be the argument to RJMP. | |||
// Need to set it properly to skip the function body. | |||
// The '- 3' is because we don't skip the PUSH, <count>, RJMP sequence. | |||
ops[1] = opcount - 3; | |||
l2_bufio_put_n(&gen->writer, ops, opcount * sizeof(l2_word)); | |||
free(w.mem); | |||
l2_gen_function(gen, pos); | |||
return 0; | |||
} | |||
static int parse_expression( | |||
struct l2_lexer *lexer, struct l2_generator *gen, struct l2_parse_error *err) { | |||
struct l2_token *tok = l2_lexer_peek(lexer, 1); | |||
@@ -33,6 +88,8 @@ static int parse_expression( | |||
l2_lexer_consume(lexer); // string | |||
l2_gen_string(gen, &str); | |||
return 0; | |||
} else if (tok->kind == L2_TOK_OPEN_BRACE) { | |||
return parse_function(lexer, gen, err); | |||
} | |||
l2_parse_err(err, tok, "In expression: Unexpected tokens %s, %s", | |||
@@ -44,17 +101,26 @@ int l2_parse_program( | |||
struct l2_lexer *lexer, struct l2_generator *gen, struct l2_parse_error *err) { | |||
l2_gen_stack_frame(gen); | |||
bool first = true; | |||
while (1) { | |||
struct l2_token *tok = l2_lexer_peek(lexer, 1); | |||
if (tok->kind == L2_TOK_EOF) { | |||
break; | |||
} | |||
// The previous expr left a value on the stack which we have to pop | |||
if (!first) { | |||
l2_gen_pop(gen); | |||
} | |||
if (parse_expression(lexer, gen, err) < 0) { | |||
l2_gen_halt(gen); | |||
l2_gen_flush(gen); | |||
return -1; | |||
} | |||
first = false; | |||
} | |||
l2_gen_halt(gen); |
@@ -57,6 +57,10 @@ void l2_vm_print_val(struct l2_vm_value *val) { | |||
printf("NAMESPACE, len %zu\n", ns->len); | |||
} | |||
break; | |||
case L2_VAL_TYPE_FUNCTION: | |||
printf("FUNCTION, pos %u, ns %u\n", val->func.pos, val->func.namespace); | |||
break; | |||
} | |||
} | |||
@@ -136,6 +140,10 @@ void l2_vm_print_op(l2_word *ops, size_t opcount, size_t *ptr) { | |||
printf("CALL\n"); | |||
break; | |||
case L2_OP_RJMP: | |||
printf("RJMP\n"); | |||
break; | |||
case L2_OP_GEN_STACK_FRAME: | |||
printf("GEN_STACK_FRAME\n"); | |||
break; | |||
@@ -184,6 +192,10 @@ void l2_vm_print_op(l2_word *ops, size_t opcount, size_t *ptr) { | |||
printf("ALLOC_NAMESPACE\n"); | |||
break; | |||
case L2_OP_ALLOC_FUNCTION: | |||
printf("ALLOC_FUNCTION\n"); | |||
break; | |||
case L2_OP_NAMESPACE_SET: | |||
printf("NAMESPACE_SET\n"); | |||
break; |
@@ -47,6 +47,8 @@ static void gc_mark(struct l2_vm *vm, l2_word id) { | |||
gc_mark_array(vm, val); | |||
} else if (typ == L2_VAL_TYPE_NAMESPACE) { | |||
gc_mark_namespace(vm, val); | |||
} else if (typ == L2_VAL_TYPE_FUNCTION) { | |||
gc_mark_namespace(vm, &vm->values[val->func.namespace]); | |||
} | |||
} | |||
@@ -196,6 +198,12 @@ void l2_vm_step(struct l2_vm *vm) { | |||
vm->iptr = word; | |||
break; | |||
case L2_OP_RJMP: | |||
word = vm->stack[vm->sptr - 1]; | |||
vm->sptr -= 1; | |||
vm->iptr += word; | |||
break; | |||
case L2_OP_GEN_STACK_FRAME: | |||
word = alloc_val(vm); | |||
vm->values[word].flags = L2_VAL_TYPE_NAMESPACE; | |||
@@ -309,6 +317,15 @@ void l2_vm_step(struct l2_vm *vm) { | |||
vm->sptr += 1; | |||
break; | |||
case L2_OP_ALLOC_FUNCTION: | |||
word = alloc_val(vm); | |||
vm->values[word].flags = L2_VAL_TYPE_FUNCTION; | |||
vm->values[word].func.pos = vm->stack[--vm->sptr]; | |||
vm->values[word].func.namespace = vm->nstack[vm->nsptr - 1]; | |||
vm->stack[vm->sptr] = word; | |||
vm->sptr += 1; | |||
break; | |||
case L2_OP_NAMESPACE_SET: | |||
{ | |||
l2_word key = vm->stack[vm->sptr - 1]; |