implement basic RAII-ish array management

This commit is contained in:
r4 2021-12-29 21:42:43 +01:00
parent 7773cc6c14
commit 4d5cd93354
8 changed files with 137 additions and 47 deletions

45
ir.c
View File

@ -51,10 +51,51 @@ void irlist_init_short(IRList *v) {
irlist_init_with_cap(v, IRLIST_INIT_CAP_SHORT); irlist_init_with_cap(v, IRLIST_INIT_CAP_SHORT);
} }
static void free_irparam(IRParam *v, bool purge);
/* if purge is set, even statically allocated literals are freed */
static void free_irparam(IRParam *v, bool purge) {
if (v->kind == IRParamLiteral)
free_value(&v->Literal, purge);
}
void irlist_term(IRList *v) { void irlist_term(IRList *v) {
for (IRItem *i = v->begin; i; i = i->next) { for (IRItem *i = v->begin; i; i = i->next) {
if (i->tok.instr == IRCallInternal && i->tok.CallI.args) switch (i->tok.instr) {
case IRSet:
case IRNeg:
case IRNot:
case IRAddrOf:
free_irparam(&i->tok.Unary.val, true);
break;
case IRAdd:
case IRSub:
case IRDiv:
case IRMul:
case IREq:
case IRNeq:
case IRLt:
case IRLe:
case IRAnd:
case IROr:
free_irparam(&i->tok.Binary.lhs, true);
free_irparam(&i->tok.Binary.rhs, true);
break;
case IRJmp:
break;
case IRJnz:
free_irparam(&i->tok.CJmp.condition, true);
break;
case IRCallInternal: {
size_t n_args = i->tok.CallI.n_args;
for (size_t j = 0; j < n_args; j++)
free_irparam(&i->tok.CallI.args[j], true);
free(i->tok.CallI.args); free(i->tok.CallI.args);
break;
}
default:
ASSERT_UNREACHED();
}
} }
pool_term(v->p); pool_term(v->p);
} }
@ -94,7 +135,7 @@ void irlist_eat_irlist(IRList *v, IRList *other) {
void irlist_update_index(IRList *v) { void irlist_update_index(IRList *v) {
if (v->index) if (v->index)
return; return;
v->index = pool_alloc(v->p, v->len); v->index = pool_alloc(v->p, sizeof(size_t) * v->len);
size_t num_idx = 0; size_t num_idx = 0;
for (IRItem *i = v->begin; i; i = i->next, num_idx++) for (IRItem *i = v->begin; i; i = i->next, num_idx++)
v->index[num_idx] = i; v->index[num_idx] = i;

5
lex.c
View File

@ -58,7 +58,7 @@ static char get_esc_char(char c) {
} }
} }
TokList lex(const char *s, Pool *static_vars) { TokList lex(const char *s) {
TokList toks; TokList toks;
toklist_init(&toks); toklist_init(&toks);
Pos pos = { .ln = 1, .col = 1 }; Pos pos = { .ln = 1, .col = 1 };
@ -329,7 +329,7 @@ TokList lex(const char *s, Pool *static_vars) {
/* go through the actual string */ /* go through the actual string */
s = start; s = start;
pos = start_pos; pos = start_pos;
char *str = pool_alloc(static_vars, type_size[TypeChar] * size); char *str = xmalloc(size);
for (size_t i = 0; i < size; i++) { for (size_t i = 0; i < size; i++) {
char c = s[0]; char c = s[0];
if (c == '\\') { if (c == '\\') {
@ -347,6 +347,7 @@ TokList lex(const char *s, Pool *static_vars) {
.type = TypeArr, .type = TypeArr,
.Arr = { .Arr = {
.is_string = true, .is_string = true,
.dynamically_allocated = false,
.type = TypeChar, .type = TypeChar,
.vals = str, .vals = str,
.len = size, .len = size,

2
lex.h
View File

@ -3,6 +3,6 @@
#include "tok.h" #include "tok.h"
TokList lex(const char *s, Pool *static_vars); TokList lex(const char *s);
#endif /* LEX_H */ #endif /* LEX_H */

8
main.c
View File

@ -166,6 +166,7 @@ static Value fn_getln(Value *args) {
.type = TypeArr, .type = TypeArr,
.Arr = { .Arr = {
.is_string = true, .is_string = true,
.dynamically_allocated = true,
.type = TypeChar, .type = TypeChar,
.vals = line, .vals = line,
.len = len, .len = len,
@ -219,11 +220,9 @@ int main(int argc, const char **argv) {
} }
fclose(fp); fclose(fp);
/* lex source file */ /* lex source file */
Pool *static_vars = pool_new(4096); TokList tokens = lex(file);
TokList tokens = lex(file, static_vars);
if (err) { if (err) {
toklist_term(&tokens); toklist_term(&tokens);
pool_term(static_vars);
free(file); free(file);
fprintf(stderr, C_IRED "Lexer error" C_RESET " in " C_CYAN "%s" C_RESET ":%zu:%zu: %s\n", filename, err_ln, err_col, errbuf); fprintf(stderr, C_IRED "Lexer error" C_RESET " in " C_CYAN "%s" C_RESET ":%zu:%zu: %s\n", filename, err_ln, err_col, errbuf);
return 1; return 1;
@ -248,7 +247,6 @@ int main(int argc, const char **argv) {
if (err) { if (err) {
irlist_term(&ir); irlist_term(&ir);
toklist_term(&tokens); toklist_term(&tokens);
pool_term(static_vars);
fprintf(stderr, C_IRED "Parser error" C_RESET " in " C_CYAN "%s" C_RESET ":%zu:%zu: %s\n", filename, err_ln, err_col, errbuf); fprintf(stderr, C_IRED "Parser error" C_RESET " in " C_CYAN "%s" C_RESET ":%zu:%zu: %s\n", filename, err_ln, err_col, errbuf);
return 1; return 1;
} }
@ -261,11 +259,9 @@ int main(int argc, const char **argv) {
run(&ir, funcs); run(&ir, funcs);
if (err) { if (err) {
irlist_term(&ir); irlist_term(&ir);
pool_term(static_vars);
fprintf(stderr, C_IRED "Runtime error" C_RESET " in " C_CYAN "%s" C_RESET ":%zu:%zu: %s\n", filename, err_ln, err_col, errbuf); fprintf(stderr, C_IRED "Runtime error" C_RESET " in " C_CYAN "%s" C_RESET ":%zu:%zu: %s\n", filename, err_ln, err_col, errbuf);
return 1; return 1;
} }
} }
irlist_term(&ir); irlist_term(&ir);
pool_term(static_vars);
} }

View File

@ -5,8 +5,6 @@
#include "map.h" #include "map.h"
#include "runtime.h" #include "runtime.h"
static BuiltinFunc *bf;
typedef struct Scope { typedef struct Scope {
struct Scope *parent; struct Scope *parent;
size_t mem_addr; size_t mem_addr;
@ -750,8 +748,6 @@ static void stmt(IRList *out_ir, TokList *toks, Map *funcs, Scope *sc, TokListIt
} }
IRList parse(TokList *toks, BuiltinFunc *builtin_funcs, size_t n_builtin_funcs) { IRList parse(TokList *toks, BuiltinFunc *builtin_funcs, size_t n_builtin_funcs) {
bf = builtin_funcs;
Map funcs; Map funcs;
map_init(&funcs, sizeof(BuiltinFunc)); map_init(&funcs, sizeof(BuiltinFunc));
for (size_t i = 0; i < n_builtin_funcs; i++) { for (size_t i = 0; i < n_builtin_funcs; i++) {

16
tok.c
View File

@ -25,6 +25,22 @@ const char *type_str[TypeEnumSize] = {
[TypeArr] = "arr", [TypeArr] = "arr",
}; };
/* if purge is set, even statically allocated literals will be freed */
void free_value(Value *v, bool purge) {
switch (v->type) {
case TypeArr:
if (v->Arr.vals && (purge || v->Arr.dynamically_allocated)) {
free(v->Arr.vals);
v->Arr.vals = NULL;
v->Arr.len = 0;
v->Arr.cap = 0;
}
break;
default:
break;
}
}
void print_value(const Value *v, bool raw) { void print_value(const Value *v, bool raw) {
switch (v->type) { switch (v->type) {
case TypeVoid: case TypeVoid:

2
tok.h
View File

@ -36,12 +36,14 @@ typedef struct Value {
struct { struct {
Type type; Type type;
bool is_string : 1; bool is_string : 1;
bool dynamically_allocated : 1;
void *vals; void *vals;
size_t len, cap; size_t len, cap;
} Arr; } Arr;
}; };
} Value; } Value;
void free_value(Value *v, bool purge);
void print_value(const Value *v, bool raw); void print_value(const Value *v, bool raw);
enum Operator { enum Operator {

66
vm.c
View File

@ -3,33 +3,50 @@
#include "runtime.h" #include "runtime.h"
#include "util.h" #include "util.h"
#define INIT_STACK_CAP 256 #define INIT_STACK_CAP 128
typedef struct Stack { typedef struct Stack {
Value *mem; Value *mem;
bool *holds_value;
size_t len, cap; size_t len, cap;
} Stack; } Stack;
static Stack stack_make(void); static Stack stack_make(void);
static void stack_term(Stack *s); static void stack_term(Stack *s);
static void stack_fit(Stack *s, size_t idx); static void stack_fit(Stack *s, size_t idx);
static void stack_assign(Stack *s, size_t idx, const Value *v);
static Stack stack_make(void) { static Stack stack_make(void) {
Stack s; Stack s;
s.mem = xmalloc(sizeof(Value) * INIT_STACK_CAP); s.mem = xmalloc(sizeof(Value) * INIT_STACK_CAP);
s.holds_value = xmalloc(sizeof(bool) * INIT_STACK_CAP);
s.cap = INIT_STACK_CAP; s.cap = INIT_STACK_CAP;
s.len = 0; s.len = 0;
for (size_t i = 0; i < s.cap; i++)
s.holds_value[i] = false;
return s; return s;
} }
static void stack_term(Stack *s) { static void stack_term(Stack *s) {
/* free any dynamically allocated objects still alive */
for (size_t i = 0; i < s->cap; i++) {
if (s->holds_value[i])
free_value(&s->mem[i], false);
}
/* free the stack memory itself */
free(s->mem); free(s->mem);
free(s->holds_value);
} }
static void stack_fit(Stack *s, size_t idx) { static void stack_fit(Stack *s, size_t idx) {
size_t size = idx+1; size_t size = idx+1;
if (size > s->cap) { if (size > s->cap) {
s->mem = xrealloc(s->mem, sizeof(Value) * (size + (s->cap *= 2))); size_t new_cap = size + s->cap * 2;
s->mem = xrealloc(s->mem, sizeof(Value) * new_cap);
s->holds_value = xrealloc(s->holds_value, sizeof(bool) * new_cap);
for (size_t i = s->cap; i < new_cap; i++)
s->holds_value[i] = false;
s->cap = new_cap;
} }
} }
@ -42,6 +59,14 @@ static Value *irparam_to_val(Stack *s, IRParam *v) {
ASSERT_UNREACHED(); ASSERT_UNREACHED();
} }
static void stack_assign(Stack *s, size_t idx, const Value *v) {
stack_fit(s, idx);
if (s->holds_value[idx])
free_value(&s->mem[idx], false); /* free any overwritten heap-allocated values */
s->mem[idx] = *v;
s->holds_value[idx] = true;
}
void run(IRList *ir, const BuiltinFunc *builtin_funcs) { void run(IRList *ir, const BuiltinFunc *builtin_funcs) {
/* so we don't have to call malloc on every function call */ /* so we don't have to call malloc on every function call */
size_t fn_args_cap = 16; size_t fn_args_cap = 16;
@ -58,25 +83,29 @@ void run(IRList *ir, const BuiltinFunc *builtin_funcs) {
switch (instr->instr) { switch (instr->instr) {
case IRSet: case IRSet:
case IRNeg: case IRNeg:
case IRNot: case IRNot: {
stack_fit(&s, instr->Unary.addr); Value res;
TRY_ELSE(s.mem[instr->Unary.addr] = eval_unary(instr->instr, irparam_to_val(&s, &instr->Unary.val)), TRY_ELSE(res = eval_unary(instr->instr, irparam_to_val(&s, &instr->Unary.val)),
{free(fn_args); stack_term(&s);}); {free(fn_args); stack_term(&s);});
stack_assign(&s, instr->Unary.addr, &res);
break; break;
case IRAddrOf: }
case IRAddrOf: {
if (instr->Unary.val.kind != IRParamAddr) { if (instr->Unary.val.kind != IRParamAddr) {
set_err("Unable to take the address of a literal"); set_err("Unable to take the address of a literal");
return; return;
} }
Value *v = &s.mem[instr->Unary.val.Addr]; Value *v = &s.mem[instr->Unary.val.Addr];
s.mem[instr->Unary.addr] = (Value){ Value res = {
.type = TypePtr, .type = TypePtr,
.Ptr = { .Ptr = {
.type = v->type, .type = v->type,
.val = &v->Void, .val = &v->Void,
}, },
}; };
stack_assign(&s, instr->Unary.addr, &res);
break; break;
}
case IRAdd: case IRAdd:
case IRSub: case IRSub:
case IRDiv: case IRDiv:
@ -86,19 +115,27 @@ void run(IRList *ir, const BuiltinFunc *builtin_funcs) {
case IRLt: case IRLt:
case IRLe: case IRLe:
case IRAnd: case IRAnd:
case IROr: case IROr: {
stack_fit(&s, instr->Binary.addr); Value res;
TRY_ELSE(s.mem[instr->Binary.addr] = eval_binary(instr->instr, TRY_ELSE(res = eval_binary(instr->instr,
irparam_to_val(&s, &instr->Binary.lhs), irparam_to_val(&s, &instr->Binary.lhs),
irparam_to_val(&s, &instr->Binary.rhs)), irparam_to_val(&s, &instr->Binary.rhs)),
{free(fn_args); stack_term(&s);}); {free(fn_args); stack_term(&s);});
stack_assign(&s, instr->Binary.addr, &res);
break; break;
}
case IRJmp: case IRJmp:
if (instr->Jmp.iaddr < ir->len)
i = ir->index[instr->Jmp.iaddr]; i = ir->index[instr->Jmp.iaddr];
else
i = NULL;
continue; continue;
case IRJnz: case IRJnz:
if (is_nonzero(irparam_to_val(&s, &instr->CJmp.condition))) { if (is_nonzero(irparam_to_val(&s, &instr->CJmp.condition))) {
i = ir->index[instr->Jmp.iaddr]; if (instr->Jmp.iaddr < ir->len)
i = ir->index[instr->CJmp.iaddr];
else
i = NULL;
continue; continue;
} }
break; break;
@ -113,16 +150,17 @@ void run(IRList *ir, const BuiltinFunc *builtin_funcs) {
fn_args[i] = *irparam_to_val(&s, &instr->CallI.args[i]); fn_args[i] = *irparam_to_val(&s, &instr->CallI.args[i]);
if (f->returns) { if (f->returns) {
stack_fit(&s, instr->CallI.ret_addr); Value res;
if (f->kind == FuncVarArgs) { if (f->kind == FuncVarArgs) {
size_t min_args = f->VarArgs.min_args; size_t min_args = f->VarArgs.min_args;
TRY_ELSE(s.mem[instr->CallI.ret_addr] = f->VarArgs.WithRet.func(n_args - min_args, fn_args), TRY_ELSE(res = f->VarArgs.WithRet.func(n_args - min_args, fn_args),
{free(fn_args); stack_term(&s);}); {free(fn_args); stack_term(&s);});
} else if (f->kind == FuncFixedArgs) { } else if (f->kind == FuncFixedArgs) {
TRY_ELSE(s.mem[instr->CallI.ret_addr] = f->FixedArgs.WithRet.func(fn_args), TRY_ELSE(res = f->FixedArgs.WithRet.func(fn_args),
{free(fn_args); stack_term(&s);}); {free(fn_args); stack_term(&s);});
} else } else
ASSERT_UNREACHED(); ASSERT_UNREACHED();
stack_assign(&s, instr->CallI.ret_addr, &res);
} else { } else {
if (f->kind == FuncVarArgs) { if (f->kind == FuncVarArgs) {
size_t min_args = f->VarArgs.min_args; size_t min_args = f->VarArgs.min_args;