Compare commits

...

9 Commits

Author SHA1 Message Date
r4
99489dfeb6 fix error message 2021-12-30 18:10:08 +01:00
r4
e7da8dfe38 make put all frees after set_err
I had the case that frees would invalidate memory set_err would later
use, so putting frees after set_err is probably good practice.
2021-12-30 18:07:59 +01:00
r4
f6b74f8f97 add generic arrays 2021-12-30 17:59:28 +01:00
r4
6f91a71306 fix memory leak + add function to convert to string 2021-12-29 22:15:56 +01:00
r4
4d5cd93354 implement basic RAII-ish array management 2021-12-29 21:42:43 +01:00
r4
7773cc6c14 change IR stream to linked list 2021-12-29 13:27:58 +01:00
r4
45feb3fe1d improve Windows detection 2021-12-28 18:03:52 +01:00
r4
ba8d2f0702 add nilptrs, more conversion functions and change how ptrs work
Pointers now no longer point to the Value struct (the internal wrapper for
values) but to the value itself.
2021-12-28 17:53:27 +01:00
r4
befce544e7 move type enum out of struct 2021-12-28 13:55:01 +01:00
14 changed files with 622 additions and 252 deletions

225
ir.c
View File

@@ -4,74 +4,145 @@
#include <stdlib.h> #include <stdlib.h>
const char *irinstr_str[IRInstrEnumSize] = { const char *irinstr_str[IRInstrEnumSize] = {
[IRSet] = "set", [IRSet] = "set",
[IRNeg] = "neg", [IRNeg] = "neg",
[IRAdd] = "add", [IRAdd] = "add",
[IRSub] = "sub", [IRSub] = "sub",
[IRMul] = "mul", [IRMul] = "mul",
[IRDiv] = "div", [IRDiv] = "div",
[IREq] = "eq", [IREq] = "eq",
[IRNeq] = "neq", [IRNeq] = "neq",
[IRLt] = "lt", [IRLt] = "lt",
[IRLe] = "le", [IRLe] = "le",
[IRNot] = "not", [IRNot] = "not",
[IRAnd] = "and", [IRAnd] = "and",
[IROr] = "or", [IROr] = "or",
[IRJmp] = "jmp", [IRJmp] = "jmp",
[IRJnz] = "jnz", [IRJnz] = "jnz",
[IRCallInternal] = "calli", [IRCallInternal] = "calli",
[IRAddrOf] = "addrof", [IRAddrOf] = "addrof",
[IRArrMake] = "mkarr",
}; };
#define IRTOKS_INIT_CAP_LONG 4096 #define IRLIST_INIT_CAP_LONG 4096
#define IRTOKS_INIT_CAP_SHORT 16 #define IRLIST_INIT_CAP_SHORT 16
static void irtoks_init_with_cap(IRToks *v, size_t cap); static void irlist_init_with_cap(IRList *v, size_t cap);
static IRItem *irlist_new_item(IRList *v);
static void irtoks_init_with_cap(IRToks *v, size_t cap) { static void irlist_init_with_cap(IRList *v, size_t cap) {
v->toks = xmalloc(sizeof(IRTok) * cap); v->begin = NULL;
v->end = NULL;
v->p = pool_new(sizeof(IRItem) * cap);
v->index = NULL;
v->len = 0; v->len = 0;
v->cap = cap;
} }
void irtoks_init_long(IRToks *v) { static IRItem *irlist_new_item(IRList *v) {
irtoks_init_with_cap(v, IRTOKS_INIT_CAP_LONG); IRItem *ret = pool_alloc(v->p, sizeof(IRItem));
ret->next = NULL;
return ret;
} }
void irtoks_init_short(IRToks *v) { void irlist_init_long(IRList *v) {
irtoks_init_with_cap(v, IRTOKS_INIT_CAP_SHORT); irlist_init_with_cap(v, IRLIST_INIT_CAP_LONG);
} }
void irtoks_term(IRToks *v) { void irlist_init_short(IRList *v) {
for (size_t i = 0; i < v->len; i++) { irlist_init_with_cap(v, IRLIST_INIT_CAP_SHORT);
if (v->toks[i].instr == IRCallInternal && v->toks[i].CallI.args) }
free(v->toks[i].CallI.args);
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) {
for (IRItem *i = v->begin; i; i = i->next) {
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:
for (size_t j = 0; j < i->tok.CallI.n_args; j++)
free_irparam(&i->tok.CallI.args[j], true);
free(i->tok.CallI.args);
break;
case IRArrMake:
for (size_t j = 0; j < i->tok.ArrMake.len; j++)
free_irparam(&i->tok.ArrMake.vals[j], true);
free(i->tok.ArrMake.vals);
break;
default:
ASSERT_UNREACHED();
}
} }
free(v->toks); pool_term(v->p);
} }
void irtoks_app(IRToks *v, IRTok t) { void irlist_app(IRList *v, IRTok t) {
if (v->len+1 > v->cap) v->index = NULL; /* invalidate index */
v->toks = xrealloc(v->toks, sizeof(IRTok) * (v->cap *= 2)); IRItem *itm = irlist_new_item(v);
v->toks[v->len++] = t; itm->tok = t;
if (!v->begin && !v->end)
v->begin = v->end = itm;
else {
v->end->next = itm;
v->end = itm;
}
v->len++;
} }
void irtoks_eat_irtoks(IRToks *v, IRToks *other, size_t jmp_offset) { void irlist_eat_irlist(IRList *v, IRList *other) {
if (v->len+other->len > v->cap) v->index = NULL; /* invalidate index */
v->toks = xrealloc(v->toks, sizeof(IRTok) * (other->len + (v->cap *= 2))); size_t jmp_offset = v->len-1;
for (size_t i = 0; i < other->len; i++) { for (IRItem *i = other->begin; i; i = i->next) {
/* correct for changed jump addresses */ /* correct for changed jump addresses */
if (other->toks[i].instr == IRJmp) if (i->tok.instr == IRJmp)
other->toks[i].Jmp.iaddr += jmp_offset; i->tok.Jmp.iaddr += jmp_offset;
else if (other->toks[i].instr == IRJnz) else if (i->tok.instr == IRJnz)
other->toks[i].CJmp.iaddr += jmp_offset; i->tok.CJmp.iaddr += jmp_offset;
v->toks[v->len++] = other->toks[i]; irlist_app(v, i->tok);
} }
/* We're not calling irtoks_term() because we don't want associated items /* We're not calling irlist_term() because we don't want associated items
* (for example function arguments) to get deallocated as well. */ * (for example function arguments) to get deallocated as well. */
free(other->toks); pool_term(other->p);
}
void irlist_update_index(IRList *v) {
if (v->index)
return;
v->index = pool_alloc(v->p, sizeof(size_t) * v->len);
size_t num_idx = 0;
for (IRItem *i = v->begin; i; i = i->next, num_idx++)
v->index[num_idx] = i;
} }
static void print_irparam(const IRParam *p); static void print_irparam(const IRParam *p);
@@ -84,17 +155,18 @@ static void print_irparam(const IRParam *p) {
} }
} }
void print_ir(IRToks *v, const BuiltinFunc *builtin_funcs) { void print_ir(IRList *v, const BuiltinFunc *builtin_funcs) {
for (size_t i = 0; i < v->len; i++) { size_t iaddr = 0;
printf("%04zx ", i); for (IRItem *i = v->begin; i; i = i->next, iaddr++) {
printf("%s", irinstr_str[v->toks[i].instr]); printf("%04zx ", iaddr);
switch (v->toks[i].instr) { printf("%s", irinstr_str[i->tok.instr]);
switch (i->tok.instr) {
case IRSet: case IRSet:
case IRNeg: case IRNeg:
case IRNot: case IRNot:
case IRAddrOf: case IRAddrOf:
printf(" %%%zx ", v->toks[i].Unary.addr); printf(" %%%zx ", i->tok.Unary.addr);
print_irparam(&v->toks[i].Unary.val); print_irparam(&i->tok.Unary.val);
break; break;
case IRAdd: case IRAdd:
case IRSub: case IRSub:
@@ -106,46 +178,55 @@ void print_ir(IRToks *v, const BuiltinFunc *builtin_funcs) {
case IRLe: case IRLe:
case IRAnd: case IRAnd:
case IROr: case IROr:
printf(" %%%zx ", v->toks[i].Binary.addr); printf(" %%%zx ", i->tok.Binary.addr);
print_irparam(&v->toks[i].Binary.lhs); print_irparam(&i->tok.Binary.lhs);
printf(" "); printf(" ");
print_irparam(&v->toks[i].Binary.rhs); print_irparam(&i->tok.Binary.rhs);
break; break;
case IRJmp: case IRJmp:
printf(" %zx", v->toks[i].Jmp.iaddr); printf(" %zx", i->tok.Jmp.iaddr);
break; break;
case IRJnz: case IRJnz:
printf(" "); printf(" ");
print_irparam(&v->toks[i].CJmp.condition); print_irparam(&i->tok.CJmp.condition);
printf(" %zx", v->toks[i].CJmp.iaddr); printf(" %zx", i->tok.CJmp.iaddr);
break; break;
case IRCallInternal: { case IRCallInternal: {
const BuiltinFunc *f = &builtin_funcs[v->toks[i].CallI.fid]; const BuiltinFunc *f = &builtin_funcs[i->tok.CallI.fid];
if (f->returns) if (f->returns)
printf(" %%%zx", v->toks[i].CallI.ret_addr); printf(" %%%zx", i->tok.CallI.ret_addr);
printf(" %s", f->name); printf(" %s", f->name);
for (size_t j = 0; j < v->toks[i].CallI.n_args; j++) { for (size_t j = 0; j < i->tok.CallI.n_args; j++) {
printf(" "); printf(" ");
print_irparam(&v->toks[i].CallI.args[j]); print_irparam(&i->tok.CallI.args[j]);
}
break;
}
case IRArrMake: {
printf(" %%%zx", i->tok.ArrMake.arr_addr);
for (size_t j = 0; j < i->tok.ArrMake.len; j++) {
printf(" ");
print_irparam(&i->tok.ArrMake.vals[j]);
} }
break; break;
} }
default: ASSERT_UNREACHED(); default: ASSERT_UNREACHED();
} }
printf(" ; %zu:%zu", v->toks[i].ln, v->toks[i].col); printf(" ; %zu:%zu", i->tok.ln, i->tok.col);
printf("\n"); printf("\n");
} }
} }
void optimize_ir(IRToks *v) { void optimize_ir(IRList *v) {
for (size_t i = 0; i < v->len; i++) { irlist_update_index(v);
switch (v->toks[i].instr) { for (IRItem *i = v->begin; i; i = i->next) {
switch (i->tok.instr) {
case IRJmp: { case IRJmp: {
/* resolve jump chains (esp. produced by if-else-if... statements) */ /* resolve jump chains (esp. produced by if-else-if... statements) */
size_t ja = i; size_t ja = i->tok.Jmp.iaddr;
while (ja < v->len && v->toks[ja].instr == IRJmp) while (ja < v->len && v->index[ja]->tok.instr == IRJmp)
ja = v->toks[ja].Jmp.iaddr; ja = v->index[ja]->tok.Jmp.iaddr;
v->toks[i].Jmp.iaddr = ja; i->tok.Jmp.iaddr = ja;
} }
default: break; default: break;
} }

37
ir.h
View File

@@ -51,6 +51,7 @@ enum IRInstr {
IRJnz, IRJnz,
IRCallInternal, IRCallInternal,
IRAddrOf, IRAddrOf,
IRArrMake,
IRInstrEnumSize, IRInstrEnumSize,
}; };
typedef enum IRInstr IRInstr; typedef enum IRInstr IRInstr;
@@ -106,22 +107,36 @@ typedef struct IRTok {
size_t n_args; size_t n_args;
IRParam *args; IRParam *args;
} CallI; } CallI;
struct {
size_t arr_addr;
size_t len, cap;
IRParam *vals;
} ArrMake;
}; };
} IRTok; } IRTok;
typedef struct IRToks { typedef struct IRItem {
size_t len, cap; struct IRItem *next;
IRTok *toks; IRTok tok;
} IRToks; } IRItem;
void irtoks_init_long(IRToks *v); typedef struct IRList {
void irtoks_init_short(IRToks *v); IRItem *begin, *end;
void irtoks_term(IRToks *v); Pool *p;
void irtoks_app(IRToks *v, IRTok t); IRItem **index; /* index to pointer, irlist_update_index() must be called before use */
void irtoks_eat_irtoks(IRToks *v, IRToks *other, size_t jmp_offset); size_t len;
} IRList;
void print_ir(IRToks *v, const BuiltinFunc *builtin_funcs); void irlist_init_long(IRList *v);
void irlist_init_short(IRList *v);
void irlist_term(IRList *v);
void irlist_app(IRList *v, IRTok t);
void irlist_eat_irlist(IRList *v, IRList *other);
void irlist_update_index(IRList *v); /* should be used very conservatively */
void optimize_ir(IRToks *v); void print_ir(IRList *v, const BuiltinFunc *builtin_funcs);
void optimize_ir(IRList *v);
#endif /* IR_H */ #endif /* IR_H */

34
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 };
@@ -81,9 +81,9 @@ TokList lex(const char *s, Pool *static_vars) {
else if (streq_0_n("while", start, i)) else if (streq_0_n("while", start, i))
emit(&toks, &pos, (Tok){ .kind = TokWhile }); emit(&toks, &pos, (Tok){ .kind = TokWhile });
else if (streq_0_n("true", start, i)) else if (streq_0_n("true", start, i))
emit(&toks, &pos, (Tok){ .kind = TokVal, .Val = { .type = { .kind = TypeBool, }, .Bool = true, }, }); emit(&toks, &pos, (Tok){ .kind = TokVal, .Val = { .type = TypeBool, .Bool = true }});
else if (streq_0_n("false", start, i)) else if (streq_0_n("false", start, i))
emit(&toks, &pos, (Tok){ .kind = TokVal, .Val = { .type = { .kind = TypeBool, }, .Bool = false, }, }); emit(&toks, &pos, (Tok){ .kind = TokVal, .Val = { .type = TypeBool, .Bool = false }});
else { else {
emit(&toks, &pos, (Tok){ emit(&toks, &pos, (Tok){
.kind = TokIdent, .kind = TokIdent,
@@ -146,9 +146,7 @@ TokList lex(const char *s, Pool *static_vars) {
emit(&toks, &pos, (Tok){ emit(&toks, &pos, (Tok){
.kind = TokVal, .kind = TokVal,
.Val = { .Val = {
.type = { .type = TypeFloat,
.kind = TypeFloat,
},
.Float = num, .Float = num,
}, },
}); });
@@ -164,9 +162,7 @@ TokList lex(const char *s, Pool *static_vars) {
emit(&toks, &pos, (Tok){ emit(&toks, &pos, (Tok){
.kind = TokVal, .kind = TokVal,
.Val = { .Val = {
.type = { .type = TypeInt,
.kind = TypeInt,
},
.Int = num, .Int = num,
}, },
}); });
@@ -234,9 +230,9 @@ TokList lex(const char *s, Pool *static_vars) {
case '!': case '!':
consume(&pos, *(s++)); consume(&pos, *(s++));
if (s[0] == '=') if (s[0] == '=')
emit(&toks, &pos, (Tok){ .kind = TokOp, .Op = OpNeq, }); emit(&toks, &pos, (Tok){ .kind = TokOp, .Op = OpNeq });
else { else {
emit(&toks, &pos, (Tok){ .kind = TokOp, .Op = OpNot, }); emit(&toks, &pos, (Tok){ .kind = TokOp, .Op = OpNot });
continue; continue;
} }
break; break;
@@ -251,6 +247,8 @@ TokList lex(const char *s, Pool *static_vars) {
case '}': case '}':
case '(': case '(':
case ')': case ')':
case '[':
case ']':
case ',': case ',':
case '+': case '+':
case '-': case '-':
@@ -301,7 +299,7 @@ TokList lex(const char *s, Pool *static_vars) {
consume(&pos, *(s++)); consume(&pos, *(s++));
c = get_esc_char(s[0]); c = get_esc_char(s[0]);
if (!c) { if (!c) {
set_err("Unrecognized escape sequence: '\\%c'", c); set_err("Unrecognized escape sequence: '\\%c'", s[0]);
return toks; return toks;
} }
} }
@@ -310,7 +308,7 @@ TokList lex(const char *s, Pool *static_vars) {
set_err("Unclosed char literal"); set_err("Unclosed char literal");
return toks; return toks;
} }
emit(&toks, &pos, (Tok){ .kind = TokVal, .Val = { .type = { .kind = TypeChar, }, .Char = c, }, }); emit(&toks, &pos, (Tok){ .kind = TokVal, .Val = { .type = TypeChar, .Char = c }});
break; break;
} }
case '"': { case '"': {
@@ -333,14 +331,15 @@ 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 == '\\') {
consume(&pos, *(s++)); consume(&pos, *(s++));
c = get_esc_char(s[0]); c = get_esc_char(s[0]);
if (!c) { if (!c) {
set_err("Unrecognized escape sequence: '\\%c'", c); set_err("Unrecognized escape sequence: '\\%c'", s[0]);
free(str);
return toks; return toks;
} }
} }
@@ -348,10 +347,11 @@ TokList lex(const char *s, Pool *static_vars) {
str[i] = c; str[i] = c;
} }
emit(&toks, &pos, (Tok){ .kind = TokVal, .Val = { emit(&toks, &pos, (Tok){ .kind = TokVal, .Val = {
.type.kind = TypeArr, .type = TypeArr,
.Arr = { .Arr = {
.is_string = true, .is_string = true,
.type.kind = TypeChar, .dynamically_allocated = false,
.type = TypeChar,
.vals = str, .vals = str,
.len = size, .len = size,
.cap = size, .cap = 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 */

122
main.c
View File

@@ -8,6 +8,7 @@
#include "ir.h" #include "ir.h"
#include "lex.h" #include "lex.h"
#include "parse.h" #include "parse.h"
#include "runtime.h"
#include "util.h" #include "util.h"
#include "vm.h" #include "vm.h"
@@ -49,16 +50,17 @@ static void fn_putln(size_t extra_args, Value *args) {
static Value fn_int(Value *args) { static Value fn_int(Value *args) {
Value ret = { Value ret = {
.type.kind = TypeInt, .type = TypeInt,
.Int = 0, .Int = 0,
}; };
switch (args[0].type.kind) { switch (args[0].type) {
case TypeVoid: break; case TypeVoid: break;
case TypeFloat: ret.Int = (ssize_t)args[0].Float; break; case TypeFloat: ret.Int = (ssize_t)args[0].Float; break;
case TypeInt: ret.Int = args[0].Int; break; case TypeInt: ret.Int = args[0].Int; break;
case TypeBool: ret.Int = (ssize_t)args[0].Bool; break; case TypeBool: ret.Int = (ssize_t)args[0].Bool; break;
case TypeChar: ret.Int = (ssize_t)args[0].Char; break;
case TypeArr: case TypeArr:
if (args[0].Arr.is_string && args[0].Arr.type.kind == TypeChar) { if (args[0].Arr.is_string && args[0].Arr.type == TypeChar) {
ssize_t endpos; ssize_t endpos;
ret.Int = stoimax((char*)args[0].Arr.vals, args[0].Arr.len, 10, &endpos); ret.Int = stoimax((char*)args[0].Arr.vals, args[0].Arr.len, 10, &endpos);
if (endpos != -1) { if (endpos != -1) {
@@ -75,16 +77,17 @@ static Value fn_int(Value *args) {
static Value fn_float(Value *args) { static Value fn_float(Value *args) {
Value ret = { Value ret = {
.type.kind = TypeFloat, .type = TypeFloat,
.Float = 0.0, .Float = 0.0,
}; };
switch (args[0].type.kind) { switch (args[0].type) {
case TypeVoid: break; case TypeVoid: break;
case TypeFloat: ret.Float = args[0].Float; break; case TypeFloat: ret.Float = args[0].Float; break;
case TypeInt: ret.Float = (double)args[0].Int; break; case TypeInt: ret.Float = (double)args[0].Int; break;
case TypeBool: ret.Float = (double)args[0].Bool; break; case TypeBool: ret.Float = (double)args[0].Bool; break;
case TypeChar: ret.Float = (double)args[0].Char; break;
case TypeArr: case TypeArr:
if (args[0].Arr.is_string && args[0].Arr.type.kind == TypeChar) { if (args[0].Arr.is_string && args[0].Arr.type == TypeChar) {
ssize_t endpos; ssize_t endpos;
ret.Float = stod((char*)args[0].Arr.vals, args[0].Arr.len, &endpos); ret.Float = stod((char*)args[0].Arr.vals, args[0].Arr.len, &endpos);
if (endpos != -1) { if (endpos != -1) {
@@ -99,19 +102,78 @@ static Value fn_float(Value *args) {
return ret; return ret;
} }
static Value fn_bool(Value *args) {
return (Value){ .type = TypeBool, .Bool = is_nonzero(&args[0]) };
}
static Value fn_char(Value *args) {
Value ret = {
.type = TypeChar,
.Float = 0.0,
};
switch (args[0].type) {
case TypeVoid: break;
case TypeFloat: ret.Char = (char)args[0].Float; break;
case TypeInt: ret.Char = (char)args[0].Int; break;
case TypeBool: ret.Char = (char)args[0].Bool; break;
case TypeChar: ret.Char = args[0].Char; break;
default: ASSERT_UNREACHED();
}
return ret;
}
static Value fn_ptr(Value *args) {
(void)args;
return (Value){ .type = TypePtr, .Ptr = { .type = TypeVoid, .val = NULL }};
}
static Value fn_string(Value *args) {
char *res = xmalloc(64);
size_t len;
switch (args[0].type) {
case TypeVoid: strcpy(res, "(void)"); len = 6; break;
case TypeFloat: len = snprintf(res, 64, "%f", args[0].Float); break;
case TypeInt: len = snprintf(res, 64, "%zd", args[0].Int); break;
case TypeBool:
if (args[0].Bool) {
strcpy(res, "true");
len = 4;
} else {
strcpy(res, "false");
len = 5;
}
break;
case TypeChar: res[0] = args[0].Char; len = 1; break;
default: ASSERT_UNREACHED();
}
return (Value){
.type = TypeArr,
.Arr = {
.is_string = true,
.dynamically_allocated = true,
.type = TypeChar,
.vals = res,
.len = len,
.cap = 64,
},
};
}
static Value fn_pow(Value *args) { static Value fn_pow(Value *args) {
if (!(args[0].type.kind == TypeFloat && args[1].type.kind == TypeFloat)) { if (!(args[0].type == TypeFloat && args[1].type == TypeFloat)) {
set_err("pow() requires arguments of type float"); set_err("pow() requires arguments of type float");
return (Value){0}; return (Value){0};
} }
return (Value){ return (Value){
.type.kind = TypeFloat, .type = TypeFloat,
.Float = pow(args[0].Float, args[1].Float), .Float = pow(args[0].Float, args[1].Float),
}; };
} }
static void fn_sleep(Value *args) { static void fn_sleep(Value *args) {
if (!(args[0].type.kind == TypeFloat && args[0].Float >= 0.0)) { if (!(args[0].type == TypeFloat && args[0].Float >= 0.0)) {
set_err("sleep() requires a positive float"); set_err("sleep() requires a positive float");
return; return;
} }
@@ -135,10 +197,11 @@ static Value fn_getln(Value *args) {
} }
return (Value){ return (Value){
.type.kind = TypeArr, .type = TypeArr,
.Arr = { .Arr = {
.is_string = true, .is_string = true,
.type.kind = TypeChar, .dynamically_allocated = true,
.type = TypeChar,
.vals = line, .vals = line,
.len = len, .len = len,
.cap = cap, .cap = cap,
@@ -191,11 +254,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;
@@ -205,19 +266,22 @@ int main(int argc, const char **argv) {
print_toks(&tokens); print_toks(&tokens);
/* parse tokens into IR code */ /* parse tokens into IR code */
BuiltinFunc funcs[] = { BuiltinFunc funcs[] = {
{ .name = "put", .kind = FuncVarArgs, .returns = false, .side_effects = true, .VarArgs = { .min_args = 0, .NoRet.func = fn_put, }}, { .name = "put", .kind = FuncVarArgs, .returns = false, .side_effects = true, .VarArgs = { .min_args = 0, .NoRet.func = fn_put, }},
{ .name = "putln", .kind = FuncVarArgs, .returns = false, .side_effects = true, .VarArgs = { .min_args = 0, .NoRet.func = fn_putln, }}, { .name = "putln", .kind = FuncVarArgs, .returns = false, .side_effects = true, .VarArgs = { .min_args = 0, .NoRet.func = fn_putln, }},
{ .name = "int", .kind = FuncFixedArgs, .returns = true, .side_effects = false, .FixedArgs = { .n_args = 1, .WithRet.func = fn_int, }}, { .name = "int", .kind = FuncFixedArgs, .returns = true, .side_effects = false, .FixedArgs = { .n_args = 1, .WithRet.func = fn_int, }},
{ .name = "float", .kind = FuncFixedArgs, .returns = true, .side_effects = false, .FixedArgs = { .n_args = 1, .WithRet.func = fn_float, }}, { .name = "float", .kind = FuncFixedArgs, .returns = true, .side_effects = false, .FixedArgs = { .n_args = 1, .WithRet.func = fn_float, }},
{ .name = "pow", .kind = FuncFixedArgs, .returns = true, .side_effects = false, .FixedArgs = { .n_args = 2, .WithRet.func = fn_pow, }}, { .name = "bool", .kind = FuncFixedArgs, .returns = true, .side_effects = false, .FixedArgs = { .n_args = 1, .WithRet.func = fn_bool, }},
{ .name = "sleep", .kind = FuncFixedArgs, .returns = false, .side_effects = true, .FixedArgs = { .n_args = 1, .NoRet.func = fn_sleep, }}, { .name = "char", .kind = FuncFixedArgs, .returns = true, .side_effects = false, .FixedArgs = { .n_args = 1, .WithRet.func = fn_char, }},
{ .name = "getln", .kind = FuncFixedArgs, .returns = true, .side_effects = true, .FixedArgs = { .n_args = 0, .WithRet.func = fn_getln, }}, { .name = "ptr", .kind = FuncFixedArgs, .returns = true, .side_effects = false, .FixedArgs = { .n_args = 0, .WithRet.func = fn_ptr, }},
{ .name = "string", .kind = FuncFixedArgs, .returns = true, .side_effects = false, .FixedArgs = { .n_args = 1, .WithRet.func = fn_string, }},
{ .name = "pow", .kind = FuncFixedArgs, .returns = true, .side_effects = false, .FixedArgs = { .n_args = 2, .WithRet.func = fn_pow, }},
{ .name = "sleep", .kind = FuncFixedArgs, .returns = false, .side_effects = true, .FixedArgs = { .n_args = 1, .NoRet.func = fn_sleep, }},
{ .name = "getln", .kind = FuncFixedArgs, .returns = true, .side_effects = true, .FixedArgs = { .n_args = 0, .WithRet.func = fn_getln, }},
}; };
IRToks ir = parse(&tokens, funcs, sizeof(funcs) / sizeof(funcs[0])); IRList ir = parse(&tokens, funcs, sizeof(funcs) / sizeof(funcs[0]));
if (err) { if (err) {
irtoks_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;
} }
@@ -229,12 +293,10 @@ int main(int argc, const char **argv) {
if (!opt_dry) { if (!opt_dry) {
run(&ir, funcs); run(&ir, funcs);
if (err) { if (err) {
irtoks_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;
} }
} }
irtoks_term(&ir); irlist_term(&ir);
pool_term(static_vars);
} }

199
parse.c
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;
@@ -32,12 +30,13 @@ static size_t get_ident_addr(const Scope *sc, const char *name, const Tok *errpo
static IRParam tok_to_irparam(Scope *sc, Tok *t); static IRParam tok_to_irparam(Scope *sc, Tok *t);
static Scope make_scope(Scope *parent, bool with_idents); static Scope make_scope(Scope *parent, bool with_idents);
static void term_scope(Scope *sc); static void term_scope(Scope *sc);
static bool expr_flush_ir_and_maybe_return(IRToks *out_ir, TokList *toks, IRTok instr, TokListItem *expr_start, Scope *expr_scope, TokListItem *t, ExprRet *out_ret); static bool expr_flush_ir_and_maybe_return(IRList *out_ir, TokList *toks, IRTok instr, TokListItem *expr_start, Scope *expr_scope, TokListItem *t, ExprRet *out_ret);
static ExprRet expr(IRToks *out_ir, TokList *toks, Map *funcs, Scope *parent_sc, TokListItem *t); static void make_value_statically_allocated(Value *v);
static void expr_into_addr(IRToks *out_ir, TokList *toks, Map *funcs, Scope *parent_sc, TokListItem *t, size_t addr); static ExprRet expr(IRList *out_ir, TokList *toks, Map *funcs, Scope *parent_sc, TokListItem *t);
static IRParam expr_into_irparam(IRToks *out_ir, TokList *toks, Map *funcs, Scope *parent_sc, TokListItem *t); static void expr_into_addr(IRList *out_ir, TokList *toks, Map *funcs, Scope *parent_sc, TokListItem *t, size_t addr);
static IRParam expr_into_irparam(IRList *out_ir, TokList *toks, Map *funcs, Scope *parent_sc, TokListItem *t);
static void skip_newlns(TokList *toks, TokListItem *from); static void skip_newlns(TokList *toks, TokListItem *from);
static void stmt(IRToks *out_ir, TokList *toks, Map *funcs, Scope *sc, TokListItem *t); static void stmt(IRList *out_ir, TokList *toks, Map *funcs, Scope *sc, TokListItem *t);
static void mark_err(const Tok *t) { static void mark_err(const Tok *t) {
err_ln = t->ln; err_ln = t->ln;
@@ -67,6 +66,9 @@ static void set_irtok_dest_addr(IRTok *t, size_t addr) {
case IRCallInternal: case IRCallInternal:
t->CallI.ret_addr = addr; t->CallI.ret_addr = addr;
break; break;
case IRArrMake:
t->ArrMake.arr_addr = addr;
break;
default: default:
ASSERT_UNREACHED(); ASSERT_UNREACHED();
} }
@@ -132,7 +134,7 @@ static void term_scope(Scope *sc) {
* If ir_tok is not the expression's last instruction, ir_tok is written to * If ir_tok is not the expression's last instruction, ir_tok is written to
* out_ir and t is replaced by a pointer to the result's memory address. * out_ir and t is replaced by a pointer to the result's memory address.
* */ * */
static bool expr_flush_ir_and_maybe_return(IRToks *out_ir, TokList *toks, IRTok ir_tok, TokListItem *expr_start, Scope *expr_scope, TokListItem *t, ExprRet *out_ret) { static bool expr_flush_ir_and_maybe_return(IRList *out_ir, TokList *toks, IRTok ir_tok, TokListItem *expr_start, Scope *expr_scope, TokListItem *t, ExprRet *out_ret) {
if (t == expr_start && t->next->tok.kind == TokOp && op_prec[t->next->tok.Op] == PREC_DELIM) { if (t == expr_start && t->next->tok.kind == TokOp && op_prec[t->next->tok.Op] == PREC_DELIM) {
/* ir_tok was the expression's last IR instruction. */ /* ir_tok was the expression's last IR instruction. */
@@ -149,7 +151,7 @@ static bool expr_flush_ir_and_maybe_return(IRToks *out_ir, TokList *toks, IRTok
size_t dest_addr = expr_scope->mem_addr++; size_t dest_addr = expr_scope->mem_addr++;
set_irtok_dest_addr(&ir_tok, dest_addr); set_irtok_dest_addr(&ir_tok, dest_addr);
irtoks_app(out_ir, ir_tok); irlist_app(out_ir, ir_tok);
t->tok = (Tok){ t->tok = (Tok){
.kind = TokIdent, .kind = TokIdent,
@@ -162,6 +164,13 @@ static bool expr_flush_ir_and_maybe_return(IRToks *out_ir, TokList *toks, IRTok
} }
} }
static void make_value_statically_allocated(Value *v) {
switch (v->type) {
case TypeArr: v->Arr.dynamically_allocated = false; break;
default: break;
}
}
/* The job of this function is to reduce the expression to the most simple form /* The job of this function is to reduce the expression to the most simple form
* writing the least IR instructions possible (without overanalyzing). * writing the least IR instructions possible (without overanalyzing).
* This means that the only IR instructions it will be writing are those for * This means that the only IR instructions it will be writing are those for
@@ -213,7 +222,7 @@ static bool expr_flush_ir_and_maybe_return(IRToks *out_ir, TokList *toks, IRTok
* l_op r_op * l_op r_op
* both l_op and r_op are delimiters (their precedence is PREC_DELIM) => done * both l_op and r_op are delimiters (their precedence is PREC_DELIM) => done
*/ */
static ExprRet expr(IRToks *out_ir, TokList *toks, Map *funcs, Scope *parent_sc, TokListItem *t) { static ExprRet expr(IRList *out_ir, TokList *toks, Map *funcs, Scope *parent_sc, TokListItem *t) {
TokListItem *start = t; TokListItem *start = t;
Scope sc = make_scope(parent_sc, false); Scope sc = make_scope(parent_sc, false);
@@ -253,7 +262,7 @@ static ExprRet expr(IRToks *out_ir, TokList *toks, Map *funcs, Scope *parent_sc,
if (r.kind == ExprRetLastInstr) { if (r.kind == ExprRetLastInstr) {
size_t res_addr = sc.mem_addr++; size_t res_addr = sc.mem_addr++;
set_irtok_dest_addr(&r.LastInstr, res_addr); set_irtok_dest_addr(&r.LastInstr, res_addr);
irtoks_app(out_ir, r.LastInstr); irlist_app(out_ir, r.LastInstr);
t->tok = (Tok){ t->tok = (Tok){
.ln = t->tok.ln, .ln = t->tok.ln,
.col = t->tok.col, .col = t->tok.col,
@@ -307,7 +316,7 @@ static ExprRet expr(IRToks *out_ir, TokList *toks, Map *funcs, Scope *parent_sc,
eval_func_in_place = false; eval_func_in_place = false;
if (t->next->tok.kind == TokOp) { if (t->next->tok.kind == TokOp) {
if (t->next->tok.Op == OpComma) { if (t->next->tok.Op == OpComma) {
toklist_del(toks, t->next, t->next); /* delete right parenthesis */ toklist_del(toks, t->next, t->next); /* delete comma */
continue; continue;
} else if (t->next->tok.Op == OpRParen) { } else if (t->next->tok.Op == OpRParen) {
toklist_del(toks, t->next, t->next); /* delete right parenthesis */ toklist_del(toks, t->next, t->next); /* delete right parenthesis */
@@ -357,6 +366,12 @@ static ExprRet expr(IRToks *out_ir, TokList *toks, Map *funcs, Scope *parent_sc,
func.VarArgs.WithRet.func(args_len - func.VarArgs.min_args, arg_vals) func.VarArgs.WithRet.func(args_len - func.VarArgs.min_args, arg_vals)
: func.FixedArgs.WithRet.func(arg_vals), : func.FixedArgs.WithRet.func(arg_vals),
}; };
/* since we have a literal return value, we want it to be fully treated like one by the memory manager */
make_value_statically_allocated(&func_ident->tok.Val);
/* immediately free any heap-allocated literals that are no longer needed */
for (size_t i = 0; i < args_len; i++)
free_value(&arg_vals[i], true);
/* free buffers */
if (arg_vals) if (arg_vals)
free(arg_vals); free(arg_vals);
if (args) if (args)
@@ -382,6 +397,99 @@ static ExprRet expr(IRToks *out_ir, TokList *toks, Map *funcs, Scope *parent_sc,
} }
} }
/* Collapse array. */
else if (t->tok.kind == TokOp && t->tok.Op == OpLBrack) {
TokListItem *lbrack = t;
bool eval_immediately = true;
size_t elems_len = 0;
size_t elems_cap = 0;
IRParam *elems = NULL;
if (t->next->tok.kind == TokOp && t->next->tok.Op == OpRBrack) {
/* empty array */
toklist_del(toks, t->next, t->next); /* delete right bracket */
} else {
elems_cap = 16;
elems = xmalloc(sizeof(IRParam) * elems_cap);
for (;;) {
if (elems_len+1 > elems_cap)
elems = xrealloc(elems, (elems_cap *= 2));
IRParam e;
TRY_RET_ELSE(e = expr_into_irparam(out_ir, toks, funcs, &sc, t->next), (ExprRet){0}, free(elems));
if (e.kind != IRParamLiteral)
eval_immediately = false;
elems[elems_len++] = e;
if (t->next->tok.kind == TokOp) {
if (t->next->tok.Op == OpComma) {
toklist_del(toks, t->next, t->next); /* delete comma */
continue;
} else if (t->next->tok.Op == OpRBrack) {
toklist_del(toks, t->next, t->next); /* delete right bracket */
break;
}
}
mark_err(&t->next->tok);
set_err("Expected ',' or ']' after array element");
free(elems);
return (ExprRet){0};
}
}
if (eval_immediately) {
/* turn array into value */
Value arr = {
.type = TypeArr,
.Arr = {
.type = TypeVoid,
.is_string = false,
.dynamically_allocated = false,
.vals = NULL,
.len = elems_len,
.cap = elems_len ? elems_cap : 0,
},
};
if (elems_len) {
Type arr_ty = elems[0].Literal.type;
void *arr_vals = xmalloc(type_size[arr_ty] * elems_cap);
for (size_t i = 0; i < elems_len; i++) {
Value *v = &elems[i].Literal;
if (v->type != arr_ty) {
set_err("Type of array item %zu (%s) differs from array type (%s)", i, type_str[v->type], type_str[arr_ty]);
free(arr_vals);
free(elems);
return (ExprRet){0};
}
memcpy((uint8_t*)arr_vals + type_size[arr_ty] * i, &v->Void, type_size[arr_ty]);
}
arr.Arr.type = arr_ty;
arr.Arr.vals = arr_vals;
}
/* set lbracket to collapsed array value */
lbrack->tok.kind = TokVal;
lbrack->tok.Val = arr;
/* free the now no longer needed element IRParam values */
free(elems);
} else {
/* array initialization IR instruction */
IRTok ir_tok = {
.ln = lbrack->tok.ln,
.col = lbrack->tok.col,
.instr = IRArrMake,
.ArrMake = {
.arr_addr = 0,
.len = elems_len,
.cap = elems_cap,
.vals = elems,
},
};
/* return if we've just evaluated the last instruction */
ExprRet ret;
if (expr_flush_ir_and_maybe_return(out_ir, toks, ir_tok, start, &sc, lbrack, &ret))
return ret;
}
}
/* Collapse unary operation. */ /* Collapse unary operation. */
if (perform_unary) { if (perform_unary) {
Tok *v = &t->tok; /* what we want to perform the operation on */ Tok *v = &t->tok; /* what we want to perform the operation on */
@@ -532,12 +640,12 @@ static ExprRet expr(IRToks *out_ir, TokList *toks, Map *funcs, Scope *parent_sc,
} }
} }
static void expr_into_addr(IRToks *out_ir, TokList *toks, Map *funcs, Scope *parent_sc, TokListItem *t, size_t addr) { static void expr_into_addr(IRList *out_ir, TokList *toks, Map *funcs, Scope *parent_sc, TokListItem *t, size_t addr) {
ExprRet r; ExprRet r;
TRY(r = expr(out_ir, toks, funcs, parent_sc, t)); TRY(r = expr(out_ir, toks, funcs, parent_sc, t));
if (r.kind == ExprRetLastInstr) { if (r.kind == ExprRetLastInstr) {
set_irtok_dest_addr(&r.LastInstr, addr); set_irtok_dest_addr(&r.LastInstr, addr);
irtoks_app(out_ir, r.LastInstr); irlist_app(out_ir, r.LastInstr);
t->tok = (Tok){ t->tok = (Tok){
.ln = t->tok.ln, .ln = t->tok.ln,
.col = t->tok.col, .col = t->tok.col,
@@ -550,7 +658,7 @@ static void expr_into_addr(IRToks *out_ir, TokList *toks, Map *funcs, Scope *par
} else if (r.kind == ExprRetVal || r.kind == ExprRetIdent) { } else if (r.kind == ExprRetVal || r.kind == ExprRetIdent) {
IRParam res; IRParam res;
TRY(res = tok_to_irparam(parent_sc, &t->tok)); TRY(res = tok_to_irparam(parent_sc, &t->tok));
irtoks_app(out_ir, (IRTok){ irlist_app(out_ir, (IRTok){
.ln = t->tok.ln, .ln = t->tok.ln,
.col = t->tok.col, .col = t->tok.col,
.instr = IRSet, .instr = IRSet,
@@ -564,14 +672,14 @@ static void expr_into_addr(IRToks *out_ir, TokList *toks, Map *funcs, Scope *par
ASSERT_UNREACHED(); ASSERT_UNREACHED();
} }
static IRParam expr_into_irparam(IRToks *out_ir, TokList *toks, Map *funcs, Scope *parent_sc, TokListItem *t) { static IRParam expr_into_irparam(IRList *out_ir, TokList *toks, Map *funcs, Scope *parent_sc, TokListItem *t) {
ExprRet r; ExprRet r;
TRY_RET(r = expr(out_ir, toks, funcs, parent_sc, t), (IRParam){0}); TRY_RET(r = expr(out_ir, toks, funcs, parent_sc, t), (IRParam){0});
if (r.kind == ExprRetLastInstr) { if (r.kind == ExprRetLastInstr) {
Scope sc = make_scope(parent_sc, false); Scope sc = make_scope(parent_sc, false);
size_t addr = sc.mem_addr++; size_t addr = sc.mem_addr++;
set_irtok_dest_addr(&r.LastInstr, addr); set_irtok_dest_addr(&r.LastInstr, addr);
irtoks_app(out_ir, r.LastInstr); irlist_app(out_ir, r.LastInstr);
return (IRParam){ return (IRParam){
.kind = IRParamAddr, .kind = IRParamAddr,
.Addr = addr, .Addr = addr,
@@ -595,7 +703,7 @@ static void skip_newlns(TokList *toks, TokListItem *from) {
toklist_del(toks, from, curr->prev); toklist_del(toks, from, curr->prev);
} }
static void stmt(IRToks *out_ir, TokList *toks, Map *funcs, Scope *sc, TokListItem *t) { static void stmt(IRList *out_ir, TokList *toks, Map *funcs, Scope *sc, TokListItem *t) {
TokListItem *start = t; TokListItem *start = t;
if (t->tok.kind == TokIdent && t->tok.Ident.kind == IdentName && (t->next->tok.kind == TokDeclare || t->next->tok.kind == TokAssign)) { if (t->tok.kind == TokIdent && t->tok.Ident.kind == IdentName && (t->next->tok.kind == TokDeclare || t->next->tok.kind == TokAssign)) {
char *name = t->tok.Ident.Name; char *name = t->tok.Ident.Name;
@@ -621,9 +729,9 @@ static void stmt(IRToks *out_ir, TokList *toks, Map *funcs, Scope *sc, TokListIt
skip_newlns(toks, t->next); skip_newlns(toks, t->next);
if (t->next->tok.kind == TokOp) { if (t->next->tok.kind == TokOp) {
if (t->next->tok.Op == OpEOF) { if (t->next->tok.Op == OpEOF) {
term_scope(&inner_sc);
mark_err(&start->tok); mark_err(&start->tok);
set_err("Unclosed '{'"); set_err("Unclosed '{'");
term_scope(&inner_sc);
return; return;
} }
if (t->next->tok.Op == OpRCurl) if (t->next->tok.Op == OpRCurl)
@@ -643,8 +751,7 @@ static void stmt(IRToks *out_ir, TokList *toks, Map *funcs, Scope *sc, TokListIt
* */ * */
/* add initial jmp instruction */ /* add initial jmp instruction */
size_t jmp_instr_iaddr = out_ir->len; irlist_app(out_ir, (IRTok){
irtoks_app(out_ir, (IRTok){
.ln = t->tok.ln, .ln = t->tok.ln,
.col = t->tok.col, .col = t->tok.col,
.instr = IRJmp, .instr = IRJmp,
@@ -652,30 +759,32 @@ static void stmt(IRToks *out_ir, TokList *toks, Map *funcs, Scope *sc, TokListIt
.iaddr = 0, /* unknown for now */ .iaddr = 0, /* unknown for now */
}, },
}); });
IRItem *jmp_instr = out_ir->end;
size_t body_iaddr = out_ir->len;
/* parse condition */ /* parse condition */
IRToks cond_ir; IRList cond_ir;
irtoks_init_short(&cond_ir); irlist_init_short(&cond_ir);
IRParam cond; IRParam cond;
TRY_ELSE(cond = expr_into_irparam(&cond_ir, toks, funcs, sc, t->next), irtoks_term(&cond_ir)); TRY_ELSE(cond = expr_into_irparam(&cond_ir, toks, funcs, sc, t->next), irlist_term(&cond_ir));
/* parse loop body */ /* parse loop body */
skip_newlns(toks, t->next); skip_newlns(toks, t->next);
TRY_ELSE(stmt(out_ir, toks, funcs, sc, t->next), irtoks_term(&cond_ir)); TRY_ELSE(stmt(out_ir, toks, funcs, sc, t->next), irlist_term(&cond_ir));
/* finally we know where the jmp from the beginning has to jump to */ /* finally we know where the jmp from the beginning has to jump to */
out_ir->toks[jmp_instr_iaddr].Jmp.iaddr = out_ir->len; jmp_instr->tok.Jmp.iaddr = out_ir->len;
/* append condition IR to program IR, then terminate condition IR stream */ /* append condition IR to program IR, then terminate condition IR stream */
irtoks_eat_irtoks(out_ir, &cond_ir, out_ir->len-1); irlist_eat_irlist(out_ir, &cond_ir);
/* add conditional jump */ /* add conditional jump */
irtoks_app(out_ir, (IRTok){ irlist_app(out_ir, (IRTok){
.ln = t->next->tok.ln, .ln = t->next->tok.ln,
.col = t->next->tok.col, .col = t->next->tok.col,
.instr = IRJnz, .instr = IRJnz,
.CJmp = { .CJmp = {
.iaddr = jmp_instr_iaddr + 1, .iaddr = body_iaddr,
.condition = cond, .condition = cond,
}, },
}); });
@@ -695,8 +804,7 @@ static void stmt(IRToks *out_ir, TokList *toks, Map *funcs, Scope *sc, TokListIt
TRY(cond = expr_into_irparam(out_ir, toks, funcs, sc, t->next)); TRY(cond = expr_into_irparam(out_ir, toks, funcs, sc, t->next));
/* add conditional jmp instruction */ /* add conditional jmp instruction */
size_t if_cjmp_instr_iaddr = out_ir->len; irlist_app(out_ir, (IRTok){
irtoks_app(out_ir, (IRTok){
.ln = t->tok.ln, .ln = t->tok.ln,
.col = t->tok.col, .col = t->tok.col,
.instr = IRJnz, .instr = IRJnz,
@@ -705,12 +813,13 @@ static void stmt(IRToks *out_ir, TokList *toks, Map *funcs, Scope *sc, TokListIt
.condition = cond, .condition = cond,
}, },
}); });
IRItem *if_cjmp_instr = out_ir->end;
/* parse if body */ /* parse if body */
skip_newlns(toks, t->next); skip_newlns(toks, t->next);
IRToks if_body; IRList if_body;
irtoks_init_short(&if_body); irlist_init_short(&if_body);
TRY_ELSE(stmt(&if_body, toks, funcs, sc, t->next), irtoks_term(&if_body)); TRY_ELSE(stmt(&if_body, toks, funcs, sc, t->next), irlist_term(&if_body));
skip_newlns(toks, t->next); skip_newlns(toks, t->next);
if (t->next->tok.kind == TokElse) { if (t->next->tok.kind == TokElse) {
@@ -718,12 +827,11 @@ static void stmt(IRToks *out_ir, TokList *toks, Map *funcs, Scope *sc, TokListIt
/* parse and add else body */ /* parse and add else body */
skip_newlns(toks, t->next); skip_newlns(toks, t->next);
TRY_ELSE(stmt(out_ir, toks, funcs, sc, t->next), irtoks_term(&if_body)); TRY_ELSE(stmt(out_ir, toks, funcs, sc, t->next), irlist_term(&if_body));
} }
/* add jmp instruction to jump back to common code */ /* add jmp instruction to jump back to common code */
size_t else_jmp_instr_iaddr = out_ir->len; irlist_app(out_ir, (IRTok){
irtoks_app(out_ir, (IRTok){
.ln = t->tok.ln, .ln = t->tok.ln,
.col = t->tok.col, .col = t->tok.col,
.instr = IRJmp, .instr = IRJmp,
@@ -731,15 +839,16 @@ static void stmt(IRToks *out_ir, TokList *toks, Map *funcs, Scope *sc, TokListIt
.iaddr = 0, /* unknown for now */ .iaddr = 0, /* unknown for now */
}, },
}); });
IRItem *else_jmp_instr = out_ir->end;
/* set if condition jmp target */ /* set if condition jmp target */
out_ir->toks[if_cjmp_instr_iaddr].CJmp.iaddr = out_ir->len; if_cjmp_instr->tok.CJmp.iaddr = out_ir->len;
/* add if body */ /* add if body */
irtoks_eat_irtoks(out_ir, &if_body, out_ir->len-1); irlist_eat_irlist(out_ir, &if_body);
/* set else jmp target */ /* set else jmp target */
out_ir->toks[else_jmp_instr_iaddr].CJmp.iaddr = out_ir->len; else_jmp_instr->tok.CJmp.iaddr = out_ir->len;
} else { } else {
/* assume expression */ /* assume expression */
TRY(expr_into_irparam(out_ir, toks, funcs, sc, t)); TRY(expr_into_irparam(out_ir, toks, funcs, sc, t));
@@ -748,9 +857,7 @@ static void stmt(IRToks *out_ir, TokList *toks, Map *funcs, Scope *sc, TokListIt
toklist_del(toks, start, t); toklist_del(toks, start, t);
} }
IRToks 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++) {
@@ -760,12 +867,12 @@ IRToks parse(TokList *toks, BuiltinFunc *builtin_funcs, size_t n_builtin_funcs)
err_ln = 0; err_col = 0; err_ln = 0; err_col = 0;
set_err("Builtin function %s() declared more than once", builtin_funcs[i].name); set_err("Builtin function %s() declared more than once", builtin_funcs[i].name);
map_term(&funcs); map_term(&funcs);
return (IRToks){0}; return (IRList){0};
} }
} }
IRToks ir; IRList ir;
irtoks_init_long(&ir); irlist_init_long(&ir);
Scope global_scope = make_scope(NULL, true); Scope global_scope = make_scope(NULL, true);
for (;;) { for (;;) {
skip_newlns(toks, toks->begin); skip_newlns(toks, toks->begin);

View File

@@ -5,6 +5,6 @@
#include "tok.h" #include "tok.h"
#include "util.h" #include "util.h"
IRToks parse(TokList *toks, BuiltinFunc *builtin_funcs, size_t n_builtin_funcs); IRList parse(TokList *toks, BuiltinFunc *builtin_funcs, size_t n_builtin_funcs);
#endif /* PARSE_H */ #endif /* PARSE_H */

View File

@@ -8,7 +8,7 @@ Value eval_binary(IRInstr instr, const Value *lhs, const Value *rhs) {
case IRSub: case IRSub:
case IRMul: case IRMul:
case IRDiv: { case IRDiv: {
if (lhs->type.kind == TypeInt && rhs->type.kind == TypeInt) { if (lhs->type == TypeInt && rhs->type == TypeInt) {
ssize_t res; ssize_t res;
switch (instr) { switch (instr) {
case IRAdd: res = lhs->Int + rhs->Int; break; case IRAdd: res = lhs->Int + rhs->Int; break;
@@ -18,10 +18,10 @@ Value eval_binary(IRInstr instr, const Value *lhs, const Value *rhs) {
default: ASSERT_UNREACHED(); default: ASSERT_UNREACHED();
} }
return (Value){ return (Value){
.type.kind = TypeInt, .type = TypeInt,
.Int = res, .Int = res,
}; };
} else if (lhs->type.kind == TypeFloat && rhs->type.kind == TypeFloat) { } else if (lhs->type == TypeFloat && rhs->type == TypeFloat) {
double res; double res;
switch (instr) { switch (instr) {
case IRAdd: res = lhs->Float + rhs->Float; break; case IRAdd: res = lhs->Float + rhs->Float; break;
@@ -31,11 +31,11 @@ Value eval_binary(IRInstr instr, const Value *lhs, const Value *rhs) {
default: ASSERT_UNREACHED(); default: ASSERT_UNREACHED();
} }
return (Value){ return (Value){
.type.kind = TypeFloat, .type = TypeFloat,
.Float = res, .Float = res,
}; };
} else { } else {
set_err("Unsupported types for operation '%s': %s and %s", irinstr_str[instr], type_str[lhs->type.kind], type_str[rhs->type.kind]); set_err("Unsupported types for operation '%s': %s and %s", irinstr_str[instr], type_str[lhs->type], type_str[rhs->type]);
return (Value){0}; return (Value){0};
} }
} }
@@ -44,7 +44,7 @@ Value eval_binary(IRInstr instr, const Value *lhs, const Value *rhs) {
case IRLt: case IRLt:
case IRLe: { case IRLe: {
bool res; bool res;
if (lhs->type.kind == TypeInt && rhs->type.kind == TypeInt) { if (lhs->type == TypeInt && rhs->type == TypeInt) {
switch (instr) { switch (instr) {
case IREq: res = lhs->Int == rhs->Int; break; case IREq: res = lhs->Int == rhs->Int; break;
case IRNeq: res = lhs->Int != rhs->Int; break; case IRNeq: res = lhs->Int != rhs->Int; break;
@@ -52,7 +52,7 @@ Value eval_binary(IRInstr instr, const Value *lhs, const Value *rhs) {
case IRLe: res = lhs->Int <= rhs->Int; break; case IRLe: res = lhs->Int <= rhs->Int; break;
default: ASSERT_UNREACHED(); default: ASSERT_UNREACHED();
}; };
} else if (lhs->type.kind == TypeFloat && rhs->type.kind == TypeFloat) { } else if (lhs->type == TypeFloat && rhs->type == TypeFloat) {
switch (instr) { switch (instr) {
case IREq: res = lhs->Float == rhs->Float; break; case IREq: res = lhs->Float == rhs->Float; break;
case IRNeq: res = lhs->Float != rhs->Float; break; case IRNeq: res = lhs->Float != rhs->Float; break;
@@ -60,8 +60,8 @@ Value eval_binary(IRInstr instr, const Value *lhs, const Value *rhs) {
case IRLe: res = lhs->Float <= rhs->Float; break; case IRLe: res = lhs->Float <= rhs->Float; break;
default: ASSERT_UNREACHED(); default: ASSERT_UNREACHED();
}; };
} else if (lhs->type.kind == TypeArr && lhs->Arr.type.kind == TypeChar && lhs->Arr.is_string && } else if (lhs->type == TypeArr && lhs->Arr.type == TypeChar && lhs->Arr.is_string &&
rhs->type.kind == TypeArr && rhs->Arr.type.kind == TypeChar && rhs->Arr.is_string) { rhs->type == TypeArr && rhs->Arr.type == TypeChar && rhs->Arr.is_string) {
switch (instr) { switch (instr) {
case IREq: case IREq:
res = lhs->Arr.len == rhs->Arr.len ? strncmp(lhs->Arr.vals, rhs->Arr.vals, lhs->Arr.len) == 0 : false; res = lhs->Arr.len == rhs->Arr.len ? strncmp(lhs->Arr.vals, rhs->Arr.vals, lhs->Arr.len) == 0 : false;
@@ -74,23 +74,23 @@ Value eval_binary(IRInstr instr, const Value *lhs, const Value *rhs) {
break; break;
}; };
} else { } else {
set_err("Unsupported types for operation '%s': %s and %s", irinstr_str[instr], type_str[lhs->type.kind], type_str[rhs->type.kind]); set_err("Unsupported types for operation '%s': %s and %s", irinstr_str[instr], type_str[lhs->type], type_str[rhs->type]);
return (Value){0}; return (Value){0};
} }
return (Value){ return (Value){
.type.kind = TypeBool, .type = TypeBool,
.Bool = res, .Bool = res,
}; };
} }
case IRAnd: case IRAnd:
return (Value){ return (Value){
.type.kind = TypeBool, .type = TypeBool,
.Bool = is_nonzero(lhs) && is_nonzero(rhs), .Bool = is_nonzero(lhs) && is_nonzero(rhs),
}; };
break; break;
case IROr: case IROr:
return (Value){ return (Value){
.type.kind = TypeBool, .type = TypeBool,
.Bool = is_nonzero(lhs) || is_nonzero(rhs), .Bool = is_nonzero(lhs) || is_nonzero(rhs),
}; };
break; break;
@@ -105,19 +105,19 @@ Value eval_unary(IRInstr instr, const Value *v) {
case IRSet: case IRSet:
return *v; return *v;
case IRNeg: case IRNeg:
if (v->type.kind == TypeInt) if (v->type == TypeInt)
return (Value){ .type.kind = TypeInt, .Int = -v->Int }; return (Value){ .type = TypeInt, .Int = -v->Int };
else if (v->type.kind == TypeFloat) else if (v->type == TypeFloat)
return (Value){ .type.kind = TypeFloat, .Float = -v->Float }; return (Value){ .type = TypeFloat, .Float = -v->Float };
else { else {
set_err("Unsupported type for operation '%s': %s", irinstr_str[instr], type_str[v->type.kind]); set_err("Unsupported type for operation '%s': %s", irinstr_str[instr], type_str[v->type]);
return (Value){0}; return (Value){0};
} }
case IRNot: case IRNot:
if (v->type.kind == TypeBool) { if (v->type == TypeBool) {
return (Value){ .type.kind = TypeBool, .Bool = !v->Bool }; return (Value){ .type = TypeBool, .Bool = !v->Bool };
} else { } else {
set_err("Unsupported type for operation '%s': %s", irinstr_str[instr], type_str[v->type.kind]); set_err("Unsupported type for operation '%s': %s", irinstr_str[instr], type_str[v->type]);
return (Value){0}; return (Value){0};
} }
case IRAddrOf: case IRAddrOf:
@@ -129,10 +129,13 @@ Value eval_unary(IRInstr instr, const Value *v) {
} }
bool is_nonzero(const Value *v) { bool is_nonzero(const Value *v) {
switch (v->type.kind) { switch (v->type) {
case TypeInt: return v->Int != 0; case TypeInt: return v->Int != 0;
case TypeFloat: return v->Float != 0.0; case TypeFloat: return v->Float != 0.0;
case TypeBool: return v->Bool; case TypeBool: return v->Bool;
case TypeChar: return v->Char != 0;
case TypePtr: return v->Ptr.val != NULL;
case TypeArr: return v->Arr.len != 0;
default: ASSERT_UNREACHED(); default: ASSERT_UNREACHED();
} }
} }
@@ -140,7 +143,7 @@ bool is_nonzero(const Value *v) {
Value zero_val(Type ty) { Value zero_val(Type ty) {
Value ret; Value ret;
ret.type = ty; ret.type = ty;
switch (ty.kind) { switch (ty) {
case TypeInt: ret.Int = 0; break; case TypeInt: ret.Int = 0; break;
case TypeFloat: ret.Float = 0.0; break; case TypeFloat: ret.Float = 0.0; break;
case TypeBool: ret.Bool = false; break; case TypeBool: ret.Bool = false; break;

40
tok.c
View File

@@ -11,6 +11,7 @@ size_t type_size[TypeEnumSize] = {
[TypeInt] = sizeof(((Value*)NULL)->Int), [TypeInt] = sizeof(((Value*)NULL)->Int),
[TypeBool] = sizeof(((Value*)NULL)->Bool), [TypeBool] = sizeof(((Value*)NULL)->Bool),
[TypeChar] = sizeof(((Value*)NULL)->Char), [TypeChar] = sizeof(((Value*)NULL)->Char),
[TypePtr] = sizeof(((Value*)NULL)->Ptr),
[TypeArr] = sizeof(((Value*)NULL)->Arr), [TypeArr] = sizeof(((Value*)NULL)->Arr),
}; };
@@ -24,8 +25,24 @@ 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.kind) { switch (v->type) {
case TypeVoid: case TypeVoid:
printf("(void)"); printf("(void)");
break; break;
@@ -47,14 +64,20 @@ void print_value(const Value *v, bool raw) {
else printf("'%c'", v->Char); else printf("'%c'", v->Char);
} }
break; break;
case TypePtr: case TypePtr: {
printf("ptr<%s>(", type_str[v->Ptr.type.kind]); if (v->Ptr.val) {
print_value(v->Ptr.val, false); printf("ptr<%s>(", type_str[v->Ptr.type]);
printf(")"); Value deref = { .type = v->Ptr.type };
memcpy(&deref.Void, v->Ptr.val, type_size[v->Ptr.type]);
print_value(&deref, false);
printf(")");
} else
printf("ptr<%s>(nil)", type_str[v->Ptr.type]);
break; break;
}
case TypeArr: case TypeArr:
if (v->Arr.is_string) { if (v->Arr.is_string) {
if (v->Arr.type.kind != TypeChar) if (v->Arr.type != TypeChar)
ASSERT_UNREACHED(); ASSERT_UNREACHED();
char *str = v->Arr.vals; char *str = v->Arr.vals;
if (!raw) if (!raw)
@@ -74,7 +97,7 @@ void print_value(const Value *v, bool raw) {
} else { } else {
printf("["); printf("[");
for (size_t i = 0;; i++) { for (size_t i = 0;; i++) {
size_t ty_sz = type_size[v->Arr.type.kind]; size_t ty_sz = type_size[v->Arr.type];
Value ty_val = { .type = v->Arr.type }; Value ty_val = { .type = v->Arr.type };
memcpy(&ty_val.Void, (uint8_t*)v->Arr.vals + ty_sz * i, ty_sz); memcpy(&ty_val.Void, (uint8_t*)v->Arr.vals + ty_sz * i, ty_sz);
print_value(&ty_val, false); print_value(&ty_val, false);
@@ -94,6 +117,7 @@ int8_t op_prec[OperatorEnumSize] = {
[OpNewLn] = PREC_DELIM, [OpNewLn] = PREC_DELIM,
[OpLCurl] = PREC_DELIM, [OpLCurl] = PREC_DELIM,
[OpRParen] = PREC_DELIM, [OpRParen] = PREC_DELIM,
[OpRBrack] = PREC_DELIM,
[OpComma] = PREC_DELIM, [OpComma] = PREC_DELIM,
[OpAnd] = 0, [OpAnd] = 0,
[OpOr] = 0, [OpOr] = 0,
@@ -114,6 +138,8 @@ const char *op_str[OperatorEnumSize] = {
[OpRCurl] = "}", [OpRCurl] = "}",
[OpLParen] = "(", [OpLParen] = "(",
[OpRParen] = ")", [OpRParen] = ")",
[OpLBrack] = "[",
[OpRBrack] = "]",
[OpComma] = ",", [OpComma] = ",",
[OpAdd] = "+", [OpAdd] = "+",
[OpSub] = "-", [OpSub] = "-",

34
tok.h
View File

@@ -5,21 +5,17 @@
#include "util.h" #include "util.h"
typedef struct Type { enum Type {
enum { TypeVoid = 0,
TypeVoid = 0, TypeFloat,
TypeFloat, TypeInt,
TypeInt, TypeBool,
TypeBool, TypeChar,
TypeChar, TypePtr,
TypePtr, TypeArr,
TypeArr, TypeEnumSize,
TypeEnumSize, };
} kind; typedef enum Type Type;
/*union {
};*/
} Type;
extern size_t type_size[TypeEnumSize]; extern size_t type_size[TypeEnumSize];
extern const char *type_str[TypeEnumSize]; extern const char *type_str[TypeEnumSize];
@@ -35,17 +31,19 @@ typedef struct Value {
char Char; char Char;
struct { struct {
Type type; Type type;
struct Value *val; void *val;
} Ptr; } Ptr;
struct { struct {
bool is_string : 1;
Type type; Type type;
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 {
@@ -53,6 +51,8 @@ enum Operator {
OpRCurl = '}', OpRCurl = '}',
OpLParen = '(', OpLParen = '(',
OpRParen = ')', OpRParen = ')',
OpLBrack = '[',
OpRBrack = ']',
OpComma = ',', OpComma = ',',
OpAdd = '+', OpAdd = '+',
OpSub = '-', OpSub = '-',

2
util.c
View File

@@ -9,7 +9,7 @@
#endif #endif
void sleep_secs(double secs) { void sleep_secs(double secs) {
#ifdef WIN32 #if defined(_WIN32) || defined(WIN32)
Sleep(secs * 1000.0); Sleep(secs * 1000.0);
#else #else
struct timespec ts; struct timespec ts;

2
util.h
View File

@@ -7,7 +7,7 @@
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#ifdef WIN32 #if defined(_WIN32) || defined(WIN32)
#include <windows.h> /* SSIZE_T */ #include <windows.h> /* SSIZE_T */
typedef SSIZE_T ssize_t; typedef SSIZE_T ssize_t;
#else #else

120
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,38 +59,55 @@ static Value *irparam_to_val(Stack *s, IRParam *v) {
ASSERT_UNREACHED(); ASSERT_UNREACHED();
} }
void run(const IRToks *ir, const BuiltinFunc *builtin_funcs) { 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) {
/* 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;
Value *fn_args = xmalloc(sizeof(Value) * fn_args_cap); Value *fn_args = xmalloc(sizeof(Value) * fn_args_cap);
/* so we can use index-based addressing */
irlist_update_index(ir);
Stack s = stack_make(); Stack s = stack_make();
for (size_t i = 0; i < ir->len;) { for (IRItem *i = ir->begin; i;) {
IRTok *instr = &ir->toks[i]; IRTok *instr = &i->tok;
err_ln = instr->ln; err_ln = instr->ln;
err_col = instr->col; err_col = instr->col;
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");
free(fn_args);
stack_term(&s);
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.kind = TypePtr, .type = TypePtr,
.Ptr = { .Ptr = {
.type.kind = v->type.kind, .type = v->type,
.val = v, .val = &v->Void,
}, },
}; };
stack_assign(&s, instr->Unary.addr, &res);
break; break;
}
case IRAdd: case IRAdd:
case IRSub: case IRSub:
case IRDiv: case IRDiv:
@@ -83,19 +117,27 @@ void run(const IRToks *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:
i = instr->Jmp.iaddr; if (instr->Jmp.iaddr < ir->len)
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 = instr->Jmp.iaddr; if (instr->Jmp.iaddr < ir->len)
i = ir->index[instr->CJmp.iaddr];
else
i = NULL;
continue; continue;
} }
break; break;
@@ -110,16 +152,17 @@ void run(const IRToks *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;
@@ -133,11 +176,44 @@ void run(const IRToks *ir, const BuiltinFunc *builtin_funcs) {
} }
break; break;
} }
case IRArrMake: {
size_t arr_len = instr->ArrMake.len, arr_cap = instr->ArrMake.cap;
Value arr = {
.type = TypeArr,
.Arr = {
.type = TypeVoid,
.is_string = false,
.dynamically_allocated = true,
.vals = NULL,
.len = arr_len,
.cap = arr_len ? arr_cap : 0,
},
};
if (arr_len) {
Type arr_ty = irparam_to_val(&s, &instr->ArrMake.vals[0])->type;
void *arr_vals = xmalloc(type_size[arr_ty] * arr_cap);
for (size_t j = 0; j < arr_len; j++) {
Value *v = irparam_to_val(&s, &instr->ArrMake.vals[j]);
if (v->type != arr_ty) {
set_err("Type of array item %zu (%s) differs from array type (%s)", j, type_str[v->type], type_str[arr_ty]);
free(arr_vals);
free(fn_args);
stack_term(&s);
return;
}
memcpy((uint8_t*)arr_vals + type_size[arr_ty] * j, &v->Void, type_size[arr_ty]);
}
arr.Arr.type = arr_ty;
arr.Arr.vals = arr_vals;
}
stack_assign(&s, instr->ArrMake.arr_addr, &arr);
break;
}
default: default:
ASSERT_UNREACHED(); ASSERT_UNREACHED();
} }
i++; i = i->next;
} }
stack_term(&s); stack_term(&s);
free(fn_args); free(fn_args);

2
vm.h
View File

@@ -3,6 +3,6 @@
#include "ir.h" #include "ir.h"
void run(const IRToks *ir, const BuiltinFunc *builtin_funcs); void run(IRList *ir, const BuiltinFunc *builtin_funcs);
#endif /* VM_H */ #endif /* VM_H */