implement basic RAII-ish array management
This commit is contained in:
parent
7773cc6c14
commit
4d5cd93354
45
ir.c
45
ir.c
@ -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
5
lex.c
@ -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
2
lex.h
@ -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
8
main.c
@ -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);
|
|
||||||
}
|
}
|
||||||
|
4
parse.c
4
parse.c
@ -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
16
tok.c
@ -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
2
tok.h
@ -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
66
vm.c
@ -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;
|
||||||
|
Loading…
Reference in New Issue
Block a user