✨ function closures.
parent
968f7a312d
commit
84f5425804
|
@ -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,29 +379,40 @@ 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);
|
||||
|
||||
prepass_run(&pre, (node_t*) node->children.data[2]);
|
||||
|
||||
// self
|
||||
sym_declare((sym_t*) fun->sym, fun_name,
|
||||
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]);
|
||||
|
||||
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);
|
||||
program_push_instr(&fun->program, OP_RET, RZ_NO_PARAM);
|
||||
|
@ -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];
|
||||
|
|
|
@ -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);
|
||||
|
|
90
lib/fun.c
90
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; i<fun->closure.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; i<fun->closure.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; i<fun->closure.size; i++)
|
||||
{
|
||||
if (fun->closure.data[i]->id == id)
|
||||
{
|
||||
return fun->closure.data[i];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
|
20
lib/fun.h
20
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
|
||||
|
|
|
@ -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; i<heap->size; 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];
|
||||
}
|
|
@ -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
|
|
@ -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)
|
||||
|
|
|
@ -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; i<locals->size; 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; i<locals->size; 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; i<locals->size; 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;
|
||||
}
|
||||
}
|
|
@ -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
|
|
@ -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);
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
21
lib/sym.c
21
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;
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
18
lib/tysy.c
18
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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
29
lib/value.c
29
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]);
|
||||
|
|
|
@ -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);
|
||||
|
|
219
lib/vm.c
219
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; i<frame->locals.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; i<vm->top_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; i<loc->size; 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; i<ARG_SZ; i++)
|
||||
{
|
||||
value_t* val = vm_pop(vm);
|
||||
args[ARG_SZ - 1 - i] = val;
|
||||
args[i] = val;
|
||||
}
|
||||
|
||||
value_t* fun_val = args[ARG_SZ - 1];
|
||||
value_t* fun_val = heap_deref(&vm->heap, 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; i<ARG_SZ; i++)
|
||||
{
|
||||
value_t* arg = args[i];
|
||||
size_t addr = fun->arg_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;
|
||||
|
||||
|
|
22
lib/vm.h
22
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);
|
||||
|
|
|
@ -48,6 +48,8 @@ roza_lib = static_library(
|
|||
|
||||
# exec
|
||||
'lib/vm.c',
|
||||
'lib/heap.c',
|
||||
'lib/locals.c',
|
||||
],
|
||||
|
||||
dependencies: [
|
||||
|
|
|
@ -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
|
||||
|
@ -58,3 +56,60 @@ fun fib(n)
|
|||
end
|
||||
|
||||
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
|
Loading…
Reference in New Issue