| @@ -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]; | |||