From 84f5425804ee44774a2a4d3cf787f72e87471a42 Mon Sep 17 00:00:00 2001 From: bog Date: Sat, 23 Dec 2023 21:17:12 +0100 Subject: [PATCH] :sparkles: function closures. --- lib/compiler.c | 48 ++++++-- lib/compiler.h | 5 +- lib/fun.c | 90 +++++++++++++++ lib/fun.h | 20 ++++ lib/heap.c | 55 ++++++++++ lib/heap.h | 19 ++++ lib/loader.c | 5 +- lib/locals.c | 90 +++++++++++++++ lib/locals.h | 25 +++++ lib/opcodes.h | 3 +- lib/prepass.c | 8 +- lib/prepass.h | 3 +- lib/sym.c | 21 ++-- lib/sym.h | 5 +- lib/type.h | 2 +- lib/tysy.c | 18 +++ lib/tysy.h | 1 + lib/value.c | 29 +++++ lib/value.h | 4 +- lib/vm.c | 219 ++++++++++++++++++++++++------------- lib/vm.h | 22 ++-- meson.build | 2 + tests/acceptances/fun.roza | 61 ++++++++++- 23 files changed, 630 insertions(+), 125 deletions(-) create mode 100644 lib/heap.c create mode 100644 lib/heap.h create mode 100644 lib/locals.c create mode 100644 lib/locals.h diff --git a/lib/compiler.c b/lib/compiler.c index 0bb7074..e745f46 100644 --- a/lib/compiler.c +++ b/lib/compiler.c @@ -7,8 +7,10 @@ #include "lib/tysy.h" #include "node.h" #include "fun.h" +#include "locals.h" void compiler_init(compiler_t* compiler, + int* id, sym_t* sym, tysy_t* tysy, err_t* err) @@ -18,11 +20,13 @@ void compiler_init(compiler_t* compiler, assert(tysy); assert(err); + compiler->parent = NULL; compiler->sym = sym; compiler->tysy = tysy; compiler->err = err; compiler->scope = 0; + compiler->id = id; } void compiler_free(compiler_t* compiler) @@ -281,7 +285,6 @@ int compiler_run(compiler_t* compiler, node_t* node, program_t* program) compiler->scope, SYM_DECL, node); - if (!entry) { char msg[RZ_STR_LIMIT]; @@ -305,6 +308,15 @@ int compiler_run(compiler_t* compiler, node_t* node, program_t* program) compiler->scope, SYM_DECL, node); + + if (0 && compiler->parent && !entry) + { + entry = sym_try_find_by_name(compiler->parent->sym, name, + compiler->scope, + SYM_DECL, + node); + } + if (!entry) { char msg[RZ_STR_LIMIT]; @@ -367,28 +379,39 @@ int compiler_run(compiler_t* compiler, node_t* node, program_t* program) assert(entry); entry->state = SYM_DECL; + entry->id = *compiler->id; // Compile function { compiler_t comp; - compiler_init(&comp, (sym_t*) fun->sym, + compiler_init(&comp, compiler->id, (sym_t*) fun->sym, compiler->tysy, compiler->err); + comp.parent = compiler; + comp.sym->parent = compiler->sym; tysolver_t tysolver; tysolver_init(&tysolver, (sym_t*) fun->sym, compiler->tysy); prepass_t pre; - prepass_init(&pre, (sym_t*) fun->sym, compiler->tysy, + prepass_init(&pre, compiler->id, (sym_t*) fun->sym, compiler->tysy, &tysolver, compiler->err); + + // self + int id = sym_declare((sym_t*) fun->sym, + (*compiler->id)++, + fun_name, + tysy_try_find_type(comp.tysy, "fun"), + comp.scope, + SYM_DECL, + node); + + + fun->base = id; + prepass_run(&pre, (node_t*) node->children.data[2]); - // self - sym_declare((sym_t*) fun->sym, fun_name, - tysy_try_find_type(comp.tysy, "fun"), - comp.scope, - SYM_DECL, - node); + fun->arg_base = *compiler->id; compiler_run(&comp, (node_t*) node->children.data[1], &fun->program); compiler_run(&comp, (node_t*) node->children.data[2], &fun->program); @@ -399,11 +422,11 @@ int compiler_run(compiler_t* compiler, node_t* node, program_t* program) compiler_free(&comp); } - // value_t* value = tysy_new_fun(compiler->tysy, fun, node->line); param_t param = program_push_new_value(program, (struct value*) value); program_push_instr(program, OP_PUSH, param); - + program_push_instr(program, OP_MKFUN, RZ_NO_PARAM); program_push_instr(program, OP_STORE, entry->id); + } break; case NODE_PARAMS: { @@ -411,7 +434,7 @@ int compiler_run(compiler_t* compiler, node_t* node, program_t* program) { node_t* child = (node_t*) node->children.data[i]; char* name = child->value.data; - sym_declare(compiler->sym, name, NULL, + sym_declare(compiler->sym, (*compiler->id)++, name, NULL, compiler->scope, SYM_DECL, child); } @@ -436,6 +459,7 @@ int compiler_run(compiler_t* compiler, node_t* node, program_t* program) compiler->scope, SYM_DECL, node); + if (!entry) { char msg[RZ_STR_LIMIT]; diff --git a/lib/compiler.h b/lib/compiler.h index 2875583..af5de86 100644 --- a/lib/compiler.h +++ b/lib/compiler.h @@ -8,15 +8,18 @@ #include "tysy.h" #include "sym.h" -typedef struct { +typedef struct compiler { + struct compiler* parent; sym_t* sym; tysy_t* tysy; err_t* err; int scope; + int* id; } compiler_t; void compiler_init(compiler_t* compiler, + int* id, sym_t* sym, tysy_t* tysy, err_t* err); diff --git a/lib/fun.c b/lib/fun.c index bd4bb35..63eab5f 100644 --- a/lib/fun.c +++ b/lib/fun.c @@ -1,12 +1,47 @@ #include "fun.h" #include "sym.h" +#include "locals.h" +#include "value.h" void fun_init(fun_t* fun, struct tysy* tysy, err_t* err) { assert(fun); program_init(&fun->program); + fun->err = err; fun->sym = malloc(sizeof(sym_t)); sym_init((sym_t*) fun->sym, (tysy_t*) tysy, err); + + fun->locals = malloc(sizeof(locals_t)); + locals_init((locals_t*) fun->locals); + + fun->base = 0; + + fun->closure.size = 0; + fun->closure.cap = 0; + fun->closure.data = NULL; +} + +fun_t* fun_new_clone(fun_t* fun) +{ + assert(fun); + fun_t* clone = malloc(sizeof(fun_t)); + fun_init(clone, fun->tysy, fun->err); + + locals_free((locals_t*) clone->locals); + free(clone->locals); + + clone->locals = (struct locals*) locals_new_clone((locals_t*) fun->locals); + clone->base = fun->base; + clone->arg_base = fun->arg_base; + + for (size_t i=0; iclosure.size; i++) + { + fun_capture(clone, + fun->closure.data[i]->id, + fun->closure.data[i]->value); + } + + return clone; } void fun_free(fun_t* fun) @@ -16,4 +51,59 @@ void fun_free(fun_t* fun) sym_free((sym_t*) fun->sym); free(fun->sym); + + locals_free((locals_t*) fun->locals); + free(fun->locals); + + for (size_t i=0; iclosure.size; i++) + { + value_free((value_t*) fun->closure.data[i]->value); + free(fun->closure.data[i]->value); + free(fun->closure.data[i]); + } + + free(fun->closure.data); + fun->closure.data = NULL; +} + +void fun_capture(fun_t* fun, int id, struct value* value) +{ + assert(fun); + assert(value); + + if (fun->closure.data == NULL) + { + fun->closure.cap = 2; + fun->closure.data = malloc(sizeof(closure_entry_t*) * fun->closure.cap); + } + + if (fun->closure.size >= fun->closure.cap) + { + fun->closure.cap *= 2; + fun->closure.data = realloc(fun->closure.data, + sizeof(closure_entry_t*) * fun->closure.cap); + } + + + fun->closure.data[fun->closure.size] = malloc(sizeof(closure_entry_t)); + fun->closure.data[fun->closure.size]->id = id; + fun->closure.data[fun->closure.size]->value = value_new_clone(value); + + fun->closure.size++; +} + +closure_entry_t* fun_try_find_closure(fun_t* fun, int id) +{ + assert(fun); + + for (size_t i=0; iclosure.size; i++) + { + if (fun->closure.data[i]->id == id) + { + return fun->closure.data[i]; + } + } + + + return NULL; } diff --git a/lib/fun.h b/lib/fun.h index 1bed6ef..a498c30 100644 --- a/lib/fun.h +++ b/lib/fun.h @@ -6,15 +6,35 @@ #include "err.h" struct sym; +struct locals; +struct value; + +typedef struct { + int id; + struct value* value; +} closure_entry_t; typedef struct { program_t program; + err_t* err; struct sym* sym; struct tysy* tysy; + struct locals* locals; + int base; + int arg_base; + struct { + size_t size; + size_t cap; + closure_entry_t** data; + } closure; + } fun_t; void fun_init(fun_t* fun, struct tysy* tysy, err_t* err); +fun_t* fun_new_clone(fun_t* fun); void fun_free(fun_t* fun); +void fun_capture(fun_t* fun, int id, struct value* value); +closure_entry_t* fun_try_find_closure(fun_t* fun, int id); #endif diff --git a/lib/heap.c b/lib/heap.c new file mode 100644 index 0000000..f355c72 --- /dev/null +++ b/lib/heap.c @@ -0,0 +1,55 @@ +#include "heap.h" + +void heap_init(heap_t* heap) +{ + assert(heap); + + heap->size = 0; + heap->cap = 0; + heap->data = NULL; +} + +void heap_free(heap_t* heap) +{ + for (size_t i=0; isize; i++) + { + //value_free(heap->data[i]); + //free(heap->data[i]); + } + + free(heap->data); + + heap->size = 0; + heap->cap = 0; + heap->data = NULL; +} + +size_t heap_alloc(heap_t* heap, value_t* value) +{ + assert(heap); + + if (heap->data == NULL) + { + heap->cap = 2; + heap->data = malloc(sizeof(value_t*) * heap->cap); + } + + if (heap->size >= heap->cap) + { + heap->cap *= 2; + heap->data = realloc(heap->data, sizeof(value_t*) * heap->cap); + } + + heap->data[heap->size] = value; + heap->size++; + + return heap->size - 1; +} + +value_t* heap_deref(heap_t* heap, size_t addr) +{ + assert(heap); + assert(addr < heap->size); + + return heap->data[addr]; +} diff --git a/lib/heap.h b/lib/heap.h new file mode 100644 index 0000000..939d04a --- /dev/null +++ b/lib/heap.h @@ -0,0 +1,19 @@ +#ifndef RZ_HEAP_H +#define RZ_HEAP_H + +#include "commons.h" +#include "value.h" + +typedef struct { + size_t cap; + size_t size; + value_t** data; +} heap_t; + +void heap_init(heap_t* heap); +void heap_free(heap_t* heap); + +size_t heap_alloc(heap_t* heap, value_t* value); +value_t* heap_deref(heap_t* heap, size_t addr); + +#endif diff --git a/lib/loader.c b/lib/loader.c index c8eb307..457b6c5 100644 --- a/lib/loader.c +++ b/lib/loader.c @@ -104,11 +104,12 @@ int loader_ldfile(loader_t* loader, char const* path, int debug) tysolver_t tysolver; tysolver_init(&tysolver, &sym, &tysy); + int id = 0; // prepass { prepass_t prepass; - prepass_init(&prepass, &sym, &tysy, &tysolver, &err); + prepass_init(&prepass, &id, &sym, &tysy, &tysolver, &err); prepass_run(&prepass, node); prepass_free(&prepass); } @@ -116,7 +117,7 @@ int loader_ldfile(loader_t* loader, char const* path, int debug) // compile { compiler_t compiler; - compiler_init(&compiler, &sym, &tysy, &err); + compiler_init(&compiler, &id, &sym, &tysy, &err); int status = compiler_run(&compiler, node, &mod.program); if (status != 0) diff --git a/lib/locals.c b/lib/locals.c new file mode 100644 index 0000000..f71a016 --- /dev/null +++ b/lib/locals.c @@ -0,0 +1,90 @@ +#include "locals.h" + +void locals_init(locals_t* locals) +{ + assert(locals); + + locals->cap = 0; + locals->size = 0; + locals->data = NULL; +} + +locals_t* locals_new_clone(locals_t* locals) +{ + assert(locals); + + locals_t* clone = malloc(sizeof(locals_t)); + locals_init(clone); + + for (size_t i=0; isize; i++) + { + locals_store(clone, + locals->data[i]->id, + locals->data[i]->addr); + } + + return clone; +} + +void locals_free(locals_t* locals) +{ + assert(locals); + + for (size_t i=0; isize; i++) + { + free(locals->data[i]); + } + + free(locals->data); + locals->data = NULL; + locals->size = 0; + locals->cap = 0; +} + +local_t* locals_try_load(locals_t* locals, int id) +{ + assert(locals); + + for (size_t i=0; isize; i++) + { + if (locals->data[i]->id == id) + { + return locals->data[i]; + } + } + + return NULL; +} + +void locals_store(locals_t* locals, int id, int addr) +{ + assert(locals); + + local_t* loc = locals_try_load(locals, id); + + if (!loc) + { + if (locals->data == NULL) + { + locals->cap = 2; + locals->data = malloc(sizeof(local_t*) * locals->cap); + } + + if (locals->size >= locals->cap) + { + locals->cap *= 2; + locals->data = realloc(locals->data, + sizeof(local_t*) * locals->cap); + } + + locals->data[locals->size] = malloc(sizeof(local_t)); + locals->data[locals->size]->id = id; + locals->data[locals->size]->addr = addr; + locals->size++; + } + else + { + loc->id = id; + loc->addr = addr; + } +} diff --git a/lib/locals.h b/lib/locals.h new file mode 100644 index 0000000..3fa650b --- /dev/null +++ b/lib/locals.h @@ -0,0 +1,25 @@ +#ifndef RZ_LOCALS_H +#define RZ_LOCALS_H + +#include "commons.h" +#include "value.h" + +typedef struct { + int id; + int addr; +} local_t; + +typedef struct { + size_t size; + size_t cap; + local_t** data; +} locals_t; + +void locals_init(locals_t* locals); +locals_t* locals_new_clone(locals_t* locals); +void locals_free(locals_t* locals); + +local_t* locals_try_load(locals_t* locals, int id); +void locals_store(locals_t* locals, int id, int addr); + +#endif diff --git a/lib/opcodes.h b/lib/opcodes.h index 3ac20ba..ca22a2b 100644 --- a/lib/opcodes.h +++ b/lib/opcodes.h @@ -14,7 +14,8 @@ G(OP_BRF), G(OP_BRT), G(OP_BR), \ G(OP_STRCAT), G(OP_STRDUP), \ G(OP_LOAD), G(OP_STORE), \ - G(OP_CALL), G(OP_RET) + G(OP_CALL), G(OP_RET), G(OP_MKFUN), \ + G(OP_ALLOC), G(OP_DEREF) RZ_ENUM_H(Opcode, OPCODES); diff --git a/lib/prepass.c b/lib/prepass.c index 567b5c9..3aecdbc 100644 --- a/lib/prepass.c +++ b/lib/prepass.c @@ -4,6 +4,7 @@ #include "node.h" void prepass_init(prepass_t* prepass, + int* id, sym_t* sym, tysy_t* tysy, tysolver_t* tysolver, @@ -21,6 +22,7 @@ void prepass_init(prepass_t* prepass, prepass->err = err; prepass->scope = 0; + prepass->id = id; } void prepass_free(prepass_t* prepass) @@ -55,7 +57,7 @@ void prepass_run(prepass_t* prepass, node_t* node) type_t* type = tysolver_try_solve_node(prepass->tysolver, (node_t*) node->children.data[1]); - sym_declare(prepass->sym, name, type, + sym_declare(prepass->sym, (*prepass->id)++, name, type, prepass->scope, SYM_PRE, node); @@ -64,7 +66,9 @@ void prepass_run(prepass_t* prepass, node_t* node) case NODE_FUNDECL: { char* name = ((node_t*) node->children.data[0])->value.data; - sym_declare(prepass->sym, name, + sym_declare(prepass->sym, + (*prepass->id)++, + name, tysy_try_find_type(prepass->tysy, "fun"), prepass->scope, SYM_PRE, diff --git a/lib/prepass.h b/lib/prepass.h index bd2516b..e288a38 100644 --- a/lib/prepass.h +++ b/lib/prepass.h @@ -13,12 +13,13 @@ typedef struct { tysy_t* tysy; tysolver_t* tysolver; err_t* err; - + int* id; int scope; } prepass_t; void prepass_init(prepass_t* prepass, + int* id, sym_t* sym, tysy_t* tysy, tysolver_t* tysolver, diff --git a/lib/sym.c b/lib/sym.c index deab2b9..1b825d9 100644 --- a/lib/sym.c +++ b/lib/sym.c @@ -4,10 +4,9 @@ void sym_init(sym_t* sym, tysy_t* tysy, err_t* err) { assert(sym); - + sym->parent = NULL; sym->tysy = tysy; sym->err = err; - sym->entries.id_counter = 0; sym->entries.size = 0; sym->entries.cap = 0; @@ -30,7 +29,7 @@ void sym_free(sym_t* sym) sym->entries.cap = 0; } -int sym_declare(sym_t* sym, char* name, type_t* type, +int sym_declare(sym_t* sym, int id, char* name, type_t* type, int scope, int state_flag, node_t* node) { assert(sym); @@ -72,9 +71,6 @@ int sym_declare(sym_t* sym, char* name, type_t* type, sym->entries.data[sym->entries.size] = malloc(sizeof(sym_entry_t)); - int id = sym->entries.id_counter; - sym->entries.id_counter++; - sym->entries.data[sym->entries.size]->name = strdup(name); sym->entries.data[sym->entries.size]->id = id; sym->entries.data[sym->entries.size]->type = type; @@ -117,9 +113,20 @@ sym_entry_t* sym_try_find_by_name(sym_t* sym, char* name, } } + sym_entry_t* result = NULL; + if (scope > 0) { - return sym_try_find_by_name(sym, name, scope - 1, state_flag, node); + result = sym_try_find_by_name(sym, name, scope - 1, state_flag, node); + + if (result) { return result; } + } + + if (sym->parent) + { + result = sym_try_find_by_name(sym->parent, name, scope, state_flag, node); + + if (result) { return result; } } return NULL; diff --git a/lib/sym.h b/lib/sym.h index f7561a8..f1a78aa 100644 --- a/lib/sym.h +++ b/lib/sym.h @@ -20,9 +20,10 @@ typedef struct { node_t* node; } sym_entry_t; -typedef struct { +typedef struct sym { tysy_t* tysy; err_t* err; + struct sym* parent; struct { int id_counter; @@ -35,7 +36,7 @@ typedef struct { void sym_init(sym_t* sym, tysy_t* tysy, err_t* err); void sym_free(sym_t* sym); -int sym_declare(sym_t* sym, char* name, type_t* type, +int sym_declare(sym_t* sym, int id, char* name, type_t* type, int scope, int state_flag, node_t* node); diff --git a/lib/type.h b/lib/type.h index f8958ac..22310d2 100644 --- a/lib/type.h +++ b/lib/type.h @@ -5,7 +5,7 @@ #define TYPE_KIND(G) \ G(TYPE_NUM), G(TYPE_BOOL), G(TYPE_STR), \ - G(TYPE_FUN) + G(TYPE_FUN), G(TYPE_REF) RZ_ENUM_H(TypeKind, TYPE_KIND); diff --git a/lib/tysy.c b/lib/tysy.c index dbadc04..bcf9259 100644 --- a/lib/tysy.c +++ b/lib/tysy.c @@ -32,6 +32,13 @@ void tysy_init(tysy_t* tysy) type_init(ty, TYPE_FUN); tysy_register_new_type(tysy, "fun", ty); } + + // ref + { + type_t* ty = malloc(sizeof(type_t)); + type_init(ty, TYPE_REF); + tysy_register_new_type(tysy, "ref", ty); + } } void tysy_free(tysy_t* tysy) @@ -119,3 +126,14 @@ value_t* tysy_new_fun(tysy_t* tysy, fun_t* fun, int line) return val; } + +value_t* tysy_new_ref(tysy_t* tysy, size_t ref, int line) +{ + assert(tysy); + + value_t* val = malloc(sizeof(value_t)); + value_init(val, tysy_try_find_type(tysy, "ref"), line); + val->value.ref = ref; + + return val; +} diff --git a/lib/tysy.h b/lib/tysy.h index cf69637..00b9457 100644 --- a/lib/tysy.h +++ b/lib/tysy.h @@ -20,5 +20,6 @@ value_t* tysy_new_num(tysy_t* tysy, double value, int line); value_t* tysy_new_bool(tysy_t* tysy, int value, int line); value_t* tysy_new_str(tysy_t* tysy, char* value, int line); value_t* tysy_new_fun(tysy_t* tysy, fun_t* fun, int line); +value_t* tysy_new_ref(tysy_t* tysy, size_t ref, int line); #endif diff --git a/lib/value.c b/lib/value.c index a427fdc..ea5af6b 100644 --- a/lib/value.c +++ b/lib/value.c @@ -12,6 +12,27 @@ void value_init(value_t* value, type_t* type, int line) value->line = line; } +value_t* value_new_clone(value_t* value) +{ + assert(value); + value_t* clone = malloc(sizeof(value_t)); + value_init(clone, value->type, value->line); + + clone->value = value->value; + + if (value->type->kind == TYPE_STR) + { + clone->value.str = strdup(value->value.str); + } + + if (value->type->kind == TYPE_FUN) + { + clone->value.fun = fun_new_clone(value->value.fun); + } + + return clone; +} + void value_free(value_t* value) { assert(value); @@ -56,6 +77,10 @@ int value_eq(value_t* value, value_t* rhs) return value->value.fun == rhs->value.fun; } break; + case TYPE_REF: { + return value->value.ref == rhs->value.ref; + } break; + default: { fprintf(stderr, "Cannot compare value of type '%s'.\n", TypeKindStr[value->type->kind]); @@ -89,6 +114,10 @@ size_t value_str(value_t* value, char* buffer, size_t size) return snprintf(buffer, size, "[fun]"); break; + case TYPE_REF: + return snprintf(buffer, size, "[ref %d]", (int) value->value.ref); + break; + default: { fprintf(stderr, "Cannot stringify unknown value of type '%s'.\n", TypeKindStr[value->type->kind]); diff --git a/lib/value.h b/lib/value.h index ab8bc21..436afee 100644 --- a/lib/value.h +++ b/lib/value.h @@ -6,12 +6,13 @@ struct program; -typedef struct { +typedef struct value { type_t* type; union { double num; int bool; + size_t ref; char* str; fun_t* fun; } value; @@ -20,6 +21,7 @@ typedef struct { } value_t; void value_init(value_t* value, type_t* type, int line); +value_t* value_new_clone(value_t* value); void value_free(value_t* value); int value_eq(value_t* value, value_t* rhs); diff --git a/lib/vm.c b/lib/vm.c index c41aaed..82a20a3 100644 --- a/lib/vm.c +++ b/lib/vm.c @@ -1,5 +1,8 @@ #include "vm.h" #include "lib/commons.h" +#include "lib/fun.h" +#include "lib/heap.h" +#include "lib/locals.h" #include "lib/mod.h" #include "lib/type.h" #include "lib/tysy.h" @@ -13,10 +16,13 @@ void vm_init(vm_t* vm, sym_t* sym, tysy_t* tysy, err_t* err) assert(tysy); vm->top_frame = NULL; + locals_init(&vm->locals); vm_frame_push(vm, NULL); vm->tysy = tysy; vm->err = err; + + heap_init(&vm->heap); } void vm_free(vm_t* vm) @@ -34,21 +40,26 @@ void vm_free(vm_t* vm) } vm->top_frame = NULL; + + heap_free(&vm->heap); + + locals_free(&vm->locals); } void vm_frame_init(frame_t* frame) { assert(frame); + frame->fun = NULL; + frame->pc = 0; frame->bp = 0; frame->sp = 0; frame->prev = NULL; frame->program = NULL; - frame->locals.cap = 0; - frame->locals.size = 0; - frame->locals.data = NULL; + + frame->locals = NULL; frame->alloc.cap = 0; frame->alloc.size = 0; @@ -57,18 +68,6 @@ void vm_frame_init(frame_t* frame) void vm_frame_free(frame_t* frame) { - for (size_t i=0; ilocals.size; i++) - { - free(frame->locals.data[i]); - } - - free(frame->locals.data); - frame->locals.data = NULL; - frame->locals.size = 0; - frame->locals.cap = 0; - - //vm_clear_alloc(frame); - if (frame->prev != NULL) { vm_frame_free((frame_t*) frame->prev); @@ -76,7 +75,7 @@ void vm_frame_free(frame_t* frame) } } -void vm_frame_push(vm_t* vm, program_t* program) +void vm_frame_push(vm_t* vm, fun_t* fun) { assert(vm); @@ -84,7 +83,17 @@ void vm_frame_push(vm_t* vm, program_t* program) vm_frame_init(frame); frame->prev = (struct frame*) vm->top_frame; - frame->program = program; + + if (fun != NULL) + { + frame->program = &fun->program; + frame->locals = (locals_t*) fun->locals; + } + else + { + frame->locals = &vm->locals; + } + vm->top_frame = frame; } @@ -92,6 +101,13 @@ int vm_frame_pop(vm_t* vm) { assert(vm); + if (vm->top_frame->fun) + { + fun_free(vm->top_frame->fun); + free(vm->top_frame->fun); + vm->top_frame->fun = NULL; + } + if (vm->top_frame) { vm_clear_alloc(vm->top_frame); @@ -113,57 +129,6 @@ int vm_frame_pop(vm_t* vm) return 0; } -local_t* vm_try_local_load(vm_t* vm, int id) -{ - assert(vm); - - for (size_t i=0; itop_frame->locals.size; i++) - { - if (vm->top_frame->locals.data[i]->id == id) - { - return vm->top_frame->locals.data[i]; - } - } - - return NULL; -} - -void vm_local_store(vm_t* vm, int id, param_t addr) -{ - assert(vm); - local_t* loc = vm_try_local_load(vm, id); - - if (!loc) - { - if (vm->top_frame->locals.data == NULL) - { - vm->top_frame->locals.cap = 2; - vm->top_frame->locals.data = malloc(sizeof(local_t*) - * vm->top_frame->locals.cap); - } - - if (vm->top_frame->locals.size >= vm->top_frame->locals.cap) - { - vm->top_frame->locals.cap *= 2; - vm->top_frame->locals.data = realloc(vm->top_frame->locals.data, - sizeof(local_t*) - * vm->top_frame->locals.cap); - } - - vm->top_frame->locals.data[vm->top_frame->locals.size] - = malloc(sizeof(local_t)); - - vm->top_frame->locals.data[vm->top_frame->locals.size]->id = id; - vm->top_frame->locals.data[vm->top_frame->locals.size]->addr = addr; - vm->top_frame->locals.size++; - } - else - { - loc->id = id; - loc->addr = addr; - } -} - void vm_push_new(vm_t* vm, value_t* value) { assert(value); @@ -465,7 +430,25 @@ int vm_exec_instr(vm_t* vm, Opcode op, param_t param) } break; case OP_LOAD: { - local_t* local = vm_try_local_load(vm, param); + local_t* local = locals_try_load(vm->top_frame->locals, param); + + if (!local && vm->top_frame->fun) + { + fun_t* fun = vm->top_frame->fun; + closure_entry_t* e = fun_try_find_closure(fun, param); + + if (e) + { + vm_push(vm, e->value); + vm->top_frame->pc++; + break; + } + } + + if (!local) + { + local = locals_try_load(&vm->locals, param); + } if (!local) { @@ -492,6 +475,7 @@ int vm_exec_instr(vm_t* vm, Opcode op, param_t param) } else { + assert(local->addr < vm->top_frame->sp); vm_push(vm, vm->top_frame->stack[local->addr]); } @@ -500,39 +484,120 @@ int vm_exec_instr(vm_t* vm, Opcode op, param_t param) case OP_STORE: { assert(vm->top_frame->sp > 0); - vm_local_store(vm, param, vm->top_frame->sp - 1); + + if (vm->top_frame->fun) + { + + fun_t* fun = vm->top_frame->fun; + + closure_entry_t* closure = fun_try_find_closure(fun, param); + + if (closure) + { + value_t* value = vm->top_frame->stack[vm->top_frame->sp - 1]; + value_free(closure->value); + free(closure->value); + closure->value = value_new_clone(value); + vm->top_frame->pc++; + break; + } + } + + locals_store(vm->top_frame->locals, param, vm->top_frame->sp - 1); + vm->top_frame->pc++; + } break; + + case OP_MKFUN: { + value_t* value = vm_pop(vm); + size_t addr = heap_alloc(&vm->heap, value); + + value_t* ref = tysy_new_ref(vm->tysy, addr, value->line); + vm_push_new(vm, ref); + + frame_t* prev = (frame_t*) vm->top_frame->prev; + locals_t* loc = &vm->locals; + + assert(value->type->kind == TYPE_FUN); + fun_t* fun = value->value.fun; + + if (vm->top_frame->fun) + { + loc = (locals_t*) vm->top_frame->fun->locals; + } + else if (prev && prev->fun) + { + loc = (locals_t*) prev->fun->locals; + } + else if (prev) + { + loc = prev->locals; + } + + if (loc) + { + for (size_t i=0; isize; i++) + { + int id = vm->top_frame->locals->data[i]->id; + value_t* loc_val = vm->top_frame->stack[addr]; + + fun_capture(fun, id, loc_val); + } + } + + vm->top_frame->pc++; + } break; + + case OP_DEREF: { + value_t* value = vm_pop(vm); + + assert(value->type->kind == TYPE_REF); + + size_t addr = value->value.ref; + + vm_push(vm, heap_deref(&vm->heap, addr)); vm->top_frame->pc++; } break; case OP_CALL: { - size_t const ARG_SZ = param + 1; + size_t const ARG_SZ = param; value_t* args[ARG_SZ]; + value_t* fun_ref = vm_pop(vm); + for (size_t i=0; iheap, fun_ref->value.ref); + assert(fun_val->type->kind == TYPE_FUN); - fun_t* fun = fun_val->value.fun; + + fun_t* fun = fun_new_clone(fun_val->value.fun); vm->top_frame->pc++; - vm_frame_push(vm, &fun->program); + vm_frame_push(vm, fun); + vm->top_frame->fun = fun; + + vm_push(vm, fun_ref); + locals_store(vm->top_frame->locals, fun->base, vm->top_frame->sp - 1); for (size_t i=0; iarg_base + (ARG_SZ - 1 - i); vm_push(vm, arg); - vm_local_store(vm, ARG_SZ - 1 - i, vm->top_frame->sp - 1); + + locals_store(vm->top_frame->locals, + addr, + vm->top_frame->sp - 1); } } break; case OP_RET: { - frame_t* frame = (frame_t*) vm->top_frame; frame_t* prev = (frame_t*) vm->top_frame->prev; diff --git a/lib/vm.h b/lib/vm.h index 966def2..113d898 100644 --- a/lib/vm.h +++ b/lib/vm.h @@ -8,11 +8,8 @@ #include "tysy.h" #include "sym.h" #include "program.h" - -typedef struct { - int id; - param_t addr; -} local_t; +#include "heap.h" +#include "locals.h" typedef struct stack_frame{ struct frame* prev; @@ -22,12 +19,8 @@ typedef struct stack_frame{ size_t pc; size_t bp; size_t sp; - - struct { - size_t size; - size_t cap; - local_t** data; - } locals; + fun_t* fun; + locals_t* locals; struct { size_t size; @@ -42,6 +35,8 @@ typedef struct { tysy_t* tysy; err_t* err; frame_t* top_frame; + heap_t heap; + locals_t locals; } vm_t; void vm_init(vm_t* vm, sym_t* sym, tysy_t* tysy, err_t* err); @@ -50,12 +45,9 @@ void vm_free(vm_t* vm); void vm_frame_init(frame_t* frame); void vm_frame_free(frame_t* frame); -void vm_frame_push(vm_t* vm, program_t* program); +void vm_frame_push(vm_t* vm, fun_t* fun); int vm_frame_pop(vm_t* vm); -local_t* vm_try_local_load(vm_t* vm, int id); -void vm_local_store(vm_t* vm, int id, param_t addr); - void vm_push_new(vm_t* vm, value_t* value); void vm_push(vm_t* vm, value_t* value); value_t* vm_pop(vm_t* vm); diff --git a/meson.build b/meson.build index 68584a4..601303d 100644 --- a/meson.build +++ b/meson.build @@ -48,6 +48,8 @@ roza_lib = static_library( # exec 'lib/vm.c', + 'lib/heap.c', + 'lib/locals.c', ], dependencies: [ diff --git a/tests/acceptances/fun.roza b/tests/acceptances/fun.roza index 777e79b..51a2a5d 100644 --- a/tests/acceptances/fun.roza +++ b/tests/acceptances/fun.roza @@ -1,6 +1,5 @@ # FUNCTIONS # ========= - fun double(n) n * 2 end @@ -38,7 +37,6 @@ assert max(7, 9) == 9 # RECURSIVITY # =========== - fun fac(n) if n == 0 then return 1 @@ -57,4 +55,61 @@ fun fib(n) return fib(n - 2) + fib(n - 1) end -assert fib(6) == 13 \ No newline at end of file +assert fib(6) == 13 + +# HIGH ORDER FUNCTIONS +# ==================== +fun fi(n) + return n + 1 +end + +fun fd(n) + return n * 2 +end + +fun twice(f, x) + return f(f(x)) +end + +assert twice(fi, 27) == 29 +assert twice(fd, 12) == 48 + +# CLOSURES +# ======== +fun make() + let counter = 0 + + fun inner() + counter = counter + 1 + return counter - 1 + end + + return inner +end + +let c = make() + +assert c() == 0 +assert c() == 1 +assert c() == 2 + +fun make_val(init) + let counter = init + + fun inner() + counter = counter + 1 + return counter - 1 + end + + return inner +end + +let d = make_val(7) +let e = make_val(3) + +assert d() == 7 +assert e() == 3 +assert d() == 8 +assert e() == 4 +assert d() == 9 +assert e() == 5 \ No newline at end of file