Compare commits
26 Commits
92c4c5c991
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
99489dfeb6 | ||
|
|
e7da8dfe38 | ||
|
|
f6b74f8f97 | ||
|
|
6f91a71306 | ||
|
|
4d5cd93354 | ||
|
|
7773cc6c14 | ||
|
|
45feb3fe1d | ||
|
|
ba8d2f0702 | ||
|
|
befce544e7 | ||
|
|
0d5313a063 | ||
|
|
22f71d7e56 | ||
|
|
a706ea6a3f | ||
|
|
0b2741f73f | ||
|
|
cda56d5b9c | ||
|
|
46e7487cad | ||
|
|
18d6e7b7df | ||
|
|
1f47b5e16c | ||
|
|
dfe1ac90e8 | ||
|
|
d8b470f0eb | ||
|
|
cf93109f1e | ||
|
|
850dafbbc9 | ||
|
|
803368a264 | ||
|
|
b4c369e1d9 | ||
|
|
dd67a1bf5d | ||
|
|
b58810e822 | ||
|
|
f02dae603d |
5
Makefile
5
Makefile
@@ -1,3 +1,6 @@
|
||||
ifneq ($(OS),Windows_NT)
|
||||
CPPFLAGS = -D_POSIX_C_SOURCE=200112L
|
||||
endif
|
||||
CFLAGS = -ggdb -std=c11 -Wall -Wextra -pedantic -Wmissing-prototypes -Wstrict-prototypes -Wold-style-definition
|
||||
#CFLAGS = -pg -std=c11 -Wall -Wextra -pedantic -Wmissing-prototypes -Wstrict-prototypes -Wold-style-definition
|
||||
#CFLAGS = -O3 -std=c11 -Wall -Wextra -pedantic -Wmissing-prototypes -Wstrict-prototypes -Wold-style-definition
|
||||
@@ -12,7 +15,7 @@ $(EXE): $(OBJ)
|
||||
$(CC) -o $@ $^ $(CFLAGS) $(LDFLAGS)
|
||||
|
||||
%.o: %.c
|
||||
$(CC) -c -o $@ $< $(CFLAGS)
|
||||
$(CC) -c -o $@ $< $(CPPFLAGS) $(CFLAGS)
|
||||
|
||||
deps.mk: $(SOURCE) $(HEADERS)
|
||||
@echo "# Automatically generated by $(CC) -MM." > $@
|
||||
|
||||
21
examples/calculator.script
Normal file
21
examples/calculator.script
Normal file
@@ -0,0 +1,21 @@
|
||||
put("Enter an operation (+, -, *, /): ")
|
||||
op := getln()
|
||||
if (!(op == "+" || op == "-" || op == "*" || op == "/")) {
|
||||
put("Unknown operation: ")
|
||||
putln(op)
|
||||
} else {
|
||||
put("1st number: ")
|
||||
n1 := float(getln())
|
||||
put("2nd number: ")
|
||||
n2 := float(getln())
|
||||
|
||||
put("Result: ")
|
||||
if (op == "+")
|
||||
putln(n1 + n2)
|
||||
else if (op == "-")
|
||||
putln(n1 - n2)
|
||||
else if (op == "*")
|
||||
putln(n1 * n2)
|
||||
else if (op == "/")
|
||||
putln(n1 / n2)
|
||||
}
|
||||
@@ -7,6 +7,6 @@ while i {
|
||||
z := x + y
|
||||
y = x
|
||||
x = z
|
||||
print(z)
|
||||
putln(z)
|
||||
i = i - 1
|
||||
}
|
||||
@@ -65,19 +65,18 @@ while y < height {
|
||||
it = it + 1
|
||||
}
|
||||
|
||||
if it <= iterations / 5 {
|
||||
if it <= iterations / 5
|
||||
put(' ')
|
||||
} else if it <= iterations / 5 * 2 {
|
||||
else if it <= iterations / 5 * 2
|
||||
put('.')
|
||||
} else if it <= iterations / 5 * 3 {
|
||||
else if it <= iterations / 5 * 3
|
||||
put(',')
|
||||
} else if it <= iterations / 5 * 4 {
|
||||
else if it <= iterations / 5 * 4
|
||||
put('*')
|
||||
} else if it < iterations {
|
||||
else if it < iterations
|
||||
put('+')
|
||||
} else if it == iterations {
|
||||
else if it == iterations
|
||||
put('#')
|
||||
}
|
||||
|
||||
x = x + 1
|
||||
}
|
||||
@@ -13,4 +13,5 @@ while k < iterations {
|
||||
k = k + 1
|
||||
}
|
||||
|
||||
print(sum)
|
||||
put("π ≈ ")
|
||||
putln(sum)
|
||||
287
ir.c
287
ir.c
@@ -4,150 +4,231 @@
|
||||
#include <stdlib.h>
|
||||
|
||||
const char *irinstr_str[IRInstrEnumSize] = {
|
||||
[IRSet] = "set",
|
||||
[IRNeg] = "neg",
|
||||
[IRAdd] = "add",
|
||||
[IRSub] = "sub",
|
||||
[IRMul] = "mul",
|
||||
[IRDiv] = "div",
|
||||
[IREq] = "eq",
|
||||
[IRLt] = "lt",
|
||||
[IRLe] = "le",
|
||||
[IRNot] = "not",
|
||||
[IRAnd] = "and",
|
||||
[IROr] = "or",
|
||||
[IRJmp] = "jmp",
|
||||
[IRJnz] = "jnz",
|
||||
[IRSet] = "set",
|
||||
[IRNeg] = "neg",
|
||||
[IRAdd] = "add",
|
||||
[IRSub] = "sub",
|
||||
[IRMul] = "mul",
|
||||
[IRDiv] = "div",
|
||||
[IREq] = "eq",
|
||||
[IRNeq] = "neq",
|
||||
[IRLt] = "lt",
|
||||
[IRLe] = "le",
|
||||
[IRNot] = "not",
|
||||
[IRAnd] = "and",
|
||||
[IROr] = "or",
|
||||
[IRJmp] = "jmp",
|
||||
[IRJnz] = "jnz",
|
||||
[IRCallInternal] = "calli",
|
||||
[IRAddrOf] = "addrof",
|
||||
[IRArrMake] = "mkarr",
|
||||
};
|
||||
|
||||
#define IRTOKS_INIT_CAP_LONG 4096
|
||||
#define IRTOKS_INIT_CAP_SHORT 16
|
||||
#define IRLIST_INIT_CAP_LONG 4096
|
||||
#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) {
|
||||
v->toks = xmalloc(sizeof(IRTok) * cap);
|
||||
static void irlist_init_with_cap(IRList *v, size_t cap) {
|
||||
v->begin = NULL;
|
||||
v->end = NULL;
|
||||
v->p = pool_new(sizeof(IRItem) * cap);
|
||||
v->index = NULL;
|
||||
v->len = 0;
|
||||
v->cap = cap;
|
||||
}
|
||||
|
||||
void irtoks_init_long(IRToks *v) {
|
||||
irtoks_init_with_cap(v, IRTOKS_INIT_CAP_LONG);
|
||||
|
||||
static IRItem *irlist_new_item(IRList *v) {
|
||||
IRItem *ret = pool_alloc(v->p, sizeof(IRItem));
|
||||
ret->next = NULL;
|
||||
return ret;
|
||||
}
|
||||
|
||||
void irtoks_init_short(IRToks *v) {
|
||||
irtoks_init_with_cap(v, IRTOKS_INIT_CAP_SHORT);
|
||||
void irlist_init_long(IRList *v) {
|
||||
irlist_init_with_cap(v, IRLIST_INIT_CAP_LONG);
|
||||
}
|
||||
|
||||
void irtoks_term(IRToks *v) {
|
||||
for (size_t i = 0; i < v->len; i++) {
|
||||
if (v->toks[i].instr == IRCallInternal && v->toks[i].CallI.args)
|
||||
free(v->toks[i].CallI.args);
|
||||
}
|
||||
free(v->toks);
|
||||
void irlist_init_short(IRList *v) {
|
||||
irlist_init_with_cap(v, IRLIST_INIT_CAP_SHORT);
|
||||
}
|
||||
|
||||
void irtoks_app(IRToks *v, IRTok t) {
|
||||
if (v->len+1 > v->cap)
|
||||
v->toks = xrealloc(v->toks, sizeof(IRTok) * (v->cap *= 2));
|
||||
v->toks[v->len++] = t;
|
||||
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 irtoks_eat_irtoks(IRToks *v, IRToks *other, size_t jmp_offset) {
|
||||
if (v->len+other->len > v->cap)
|
||||
v->toks = xrealloc(v->toks, sizeof(IRTok) * (other->len + (v->cap *= 2)));
|
||||
for (size_t i = 0; i < other->len; i++) {
|
||||
/* correct for changed jump addresses */
|
||||
if (other->toks[i].instr == IRJmp)
|
||||
other->toks[i].Jmp.iaddr += jmp_offset;
|
||||
else if (other->toks[i].instr == IRJnz)
|
||||
other->toks[i].CJmp.iaddr += jmp_offset;
|
||||
|
||||
v->toks[v->len++] = other->toks[i];
|
||||
}
|
||||
/* We're not calling irtoks_term() because we don't want associated items
|
||||
* (for example function arguments) to get deallocated as well. */
|
||||
free(other->toks);
|
||||
}
|
||||
|
||||
static void print_val(const Value *v);
|
||||
static void print_irparam(const IRParam *p);
|
||||
|
||||
static void print_val(const Value *v) {
|
||||
switch (v->type.kind) {
|
||||
case TypeFloat:
|
||||
printf("%f", v->Float);
|
||||
break;
|
||||
case TypeInt:
|
||||
printf("%zd", v->Int);
|
||||
break;
|
||||
case TypeBool:
|
||||
printf("%s", v->Bool ? "true" : "false");
|
||||
break;
|
||||
case TypeChar:
|
||||
printf("'%c'", v->Char);
|
||||
break;
|
||||
default:
|
||||
printf("(unknown type)");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void print_irparam(const IRParam *p) {
|
||||
if (p->kind == IRParamLiteral) {
|
||||
print_val(&p->Literal);
|
||||
} else if (p->kind == IRParamAddr) {
|
||||
printf("%%%zd", p->Addr);
|
||||
}
|
||||
}
|
||||
|
||||
void print_ir(IRToks *v, const BuiltinFunc *builtin_funcs) {
|
||||
for (size_t i = 0; i < v->len; i++) {
|
||||
printf("%04zx ", i);
|
||||
printf("%s", irinstr_str[v->toks[i].instr]);
|
||||
switch (v->toks[i].instr) {
|
||||
void irlist_term(IRList *v) {
|
||||
for (IRItem *i = v->begin; i; i = i->next) {
|
||||
switch (i->tok.instr) {
|
||||
case IRSet:
|
||||
case IRNeg:
|
||||
case IRNot:
|
||||
printf(" %%%zx ", v->toks[i].Unary.addr);
|
||||
print_irparam(&v->toks[i].Unary.val);
|
||||
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:
|
||||
printf(" %%%zx ", v->toks[i].Binary.addr);
|
||||
print_irparam(&v->toks[i].Binary.lhs);
|
||||
printf(" ");
|
||||
print_irparam(&v->toks[i].Binary.rhs);
|
||||
free_irparam(&i->tok.Binary.lhs, true);
|
||||
free_irparam(&i->tok.Binary.rhs, true);
|
||||
break;
|
||||
case IRJmp:
|
||||
printf(" %zx", v->toks[i].Jmp.iaddr);
|
||||
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();
|
||||
}
|
||||
}
|
||||
pool_term(v->p);
|
||||
}
|
||||
|
||||
void irlist_app(IRList *v, IRTok t) {
|
||||
v->index = NULL; /* invalidate index */
|
||||
IRItem *itm = irlist_new_item(v);
|
||||
itm->tok = t;
|
||||
|
||||
if (!v->begin && !v->end)
|
||||
v->begin = v->end = itm;
|
||||
else {
|
||||
v->end->next = itm;
|
||||
v->end = itm;
|
||||
}
|
||||
|
||||
v->len++;
|
||||
}
|
||||
|
||||
void irlist_eat_irlist(IRList *v, IRList *other) {
|
||||
v->index = NULL; /* invalidate index */
|
||||
size_t jmp_offset = v->len-1;
|
||||
for (IRItem *i = other->begin; i; i = i->next) {
|
||||
/* correct for changed jump addresses */
|
||||
if (i->tok.instr == IRJmp)
|
||||
i->tok.Jmp.iaddr += jmp_offset;
|
||||
else if (i->tok.instr == IRJnz)
|
||||
i->tok.CJmp.iaddr += jmp_offset;
|
||||
|
||||
irlist_app(v, i->tok);
|
||||
}
|
||||
/* We're not calling irlist_term() because we don't want associated items
|
||||
* (for example function arguments) to get deallocated as well. */
|
||||
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) {
|
||||
if (p->kind == IRParamLiteral) {
|
||||
print_value(&p->Literal, false);
|
||||
} else if (p->kind == IRParamAddr) {
|
||||
printf("%%%zd", p->Addr);
|
||||
}
|
||||
}
|
||||
|
||||
void print_ir(IRList *v, const BuiltinFunc *builtin_funcs) {
|
||||
size_t iaddr = 0;
|
||||
for (IRItem *i = v->begin; i; i = i->next, iaddr++) {
|
||||
printf("%04zx ", iaddr);
|
||||
printf("%s", irinstr_str[i->tok.instr]);
|
||||
switch (i->tok.instr) {
|
||||
case IRSet:
|
||||
case IRNeg:
|
||||
case IRNot:
|
||||
case IRAddrOf:
|
||||
printf(" %%%zx ", i->tok.Unary.addr);
|
||||
print_irparam(&i->tok.Unary.val);
|
||||
break;
|
||||
case IRAdd:
|
||||
case IRSub:
|
||||
case IRDiv:
|
||||
case IRMul:
|
||||
case IREq:
|
||||
case IRNeq:
|
||||
case IRLt:
|
||||
case IRLe:
|
||||
case IRAnd:
|
||||
case IROr:
|
||||
printf(" %%%zx ", i->tok.Binary.addr);
|
||||
print_irparam(&i->tok.Binary.lhs);
|
||||
printf(" ");
|
||||
print_irparam(&i->tok.Binary.rhs);
|
||||
break;
|
||||
case IRJmp:
|
||||
printf(" %zx", i->tok.Jmp.iaddr);
|
||||
break;
|
||||
case IRJnz:
|
||||
printf(" ");
|
||||
print_irparam(&v->toks[i].CJmp.condition);
|
||||
printf(" %zx", v->toks[i].CJmp.iaddr);
|
||||
print_irparam(&i->tok.CJmp.condition);
|
||||
printf(" %zx", i->tok.CJmp.iaddr);
|
||||
break;
|
||||
case IRCallInternal: {
|
||||
const BuiltinFunc *f = &builtin_funcs[v->toks[i].CallI.fid];
|
||||
printf(" %s %%%zx", f->name, v->toks[i].CallI.ret_addr);
|
||||
for (size_t j = 0; j < f->n_args; j++) {
|
||||
const BuiltinFunc *f = &builtin_funcs[i->tok.CallI.fid];
|
||||
if (f->returns)
|
||||
printf(" %%%zx", i->tok.CallI.ret_addr);
|
||||
printf(" %s", f->name);
|
||||
for (size_t j = 0; j < i->tok.CallI.n_args; j++) {
|
||||
printf(" ");
|
||||
print_irparam(&v->toks[i].CallI.args[j]);
|
||||
print_irparam(&i->tok.CallI.args[j]);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: ASSERT_UNREACHED(); 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;
|
||||
}
|
||||
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");
|
||||
}
|
||||
}
|
||||
|
||||
void optimize_ir(IRList *v) {
|
||||
irlist_update_index(v);
|
||||
for (IRItem *i = v->begin; i; i = i->next) {
|
||||
switch (i->tok.instr) {
|
||||
case IRJmp: {
|
||||
/* resolve jump chains (esp. produced by if-else-if... statements) */
|
||||
size_t ja = i->tok.Jmp.iaddr;
|
||||
while (ja < v->len && v->index[ja]->tok.instr == IRJmp)
|
||||
ja = v->index[ja]->tok.Jmp.iaddr;
|
||||
i->tok.Jmp.iaddr = ja;
|
||||
}
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
68
ir.h
68
ir.h
@@ -4,11 +4,33 @@
|
||||
#include "tok.h"
|
||||
|
||||
typedef struct BuiltinFunc {
|
||||
char *name;
|
||||
enum {
|
||||
FuncFixedArgs,
|
||||
FuncVarArgs,
|
||||
} kind;
|
||||
|
||||
bool returns : 1;
|
||||
bool side_effects : 1;
|
||||
size_t n_args;
|
||||
Value (*func)(Value *args);
|
||||
char *name;
|
||||
size_t fid; /* function ID, assigned automatically */
|
||||
|
||||
union {
|
||||
struct {
|
||||
size_t n_args;
|
||||
union {
|
||||
struct { Value (*func)(Value *args); } WithRet;
|
||||
struct { void (*func)(Value *args); } NoRet;
|
||||
};
|
||||
} FixedArgs;
|
||||
|
||||
struct {
|
||||
size_t min_args;
|
||||
union {
|
||||
struct { Value (*func)(size_t extra_args, Value *args); } WithRet;
|
||||
struct { void (*func)(size_t extra_args, Value *args); } NoRet;
|
||||
};
|
||||
} VarArgs;
|
||||
};
|
||||
} BuiltinFunc;
|
||||
|
||||
enum IRInstr {
|
||||
@@ -19,6 +41,7 @@ enum IRInstr {
|
||||
IRMul,
|
||||
IRDiv,
|
||||
IREq,
|
||||
IRNeq,
|
||||
IRLt,
|
||||
IRLe,
|
||||
IRNot,
|
||||
@@ -27,6 +50,8 @@ enum IRInstr {
|
||||
IRJmp,
|
||||
IRJnz,
|
||||
IRCallInternal,
|
||||
IRAddrOf,
|
||||
IRArrMake,
|
||||
IRInstrEnumSize,
|
||||
};
|
||||
typedef enum IRInstr IRInstr;
|
||||
@@ -79,22 +104,39 @@ typedef struct IRTok {
|
||||
struct {
|
||||
size_t ret_addr;
|
||||
size_t fid;
|
||||
size_t n_args;
|
||||
IRParam *args;
|
||||
} CallI;
|
||||
|
||||
struct {
|
||||
size_t arr_addr;
|
||||
size_t len, cap;
|
||||
IRParam *vals;
|
||||
} ArrMake;
|
||||
};
|
||||
} IRTok;
|
||||
|
||||
typedef struct IRToks {
|
||||
size_t len, cap;
|
||||
IRTok *toks;
|
||||
} IRToks;
|
||||
typedef struct IRItem {
|
||||
struct IRItem *next;
|
||||
IRTok tok;
|
||||
} IRItem;
|
||||
|
||||
void irtoks_init_long(IRToks *v);
|
||||
void irtoks_init_short(IRToks *v);
|
||||
void irtoks_term(IRToks *v);
|
||||
void irtoks_app(IRToks *v, IRTok t);
|
||||
void irtoks_eat_irtoks(IRToks *v, IRToks *other, size_t jmp_offset);
|
||||
typedef struct IRList {
|
||||
IRItem *begin, *end;
|
||||
Pool *p;
|
||||
IRItem **index; /* index to pointer, irlist_update_index() must be called before use */
|
||||
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 print_ir(IRList *v, const BuiltinFunc *builtin_funcs);
|
||||
|
||||
void optimize_ir(IRList *v);
|
||||
|
||||
#endif /* IR_H */
|
||||
|
||||
92
lex.c
92
lex.c
@@ -2,6 +2,8 @@
|
||||
|
||||
#include "util.h"
|
||||
|
||||
#define TAB_WIDTH 4
|
||||
|
||||
typedef struct Pos {
|
||||
size_t ln, col; /* current position */
|
||||
size_t m_ln, m_col; /* marked position */
|
||||
@@ -17,7 +19,9 @@ static void consume(Pos *p, char c) {
|
||||
if (c == '\n') {
|
||||
p->ln++;
|
||||
p->col = 1;
|
||||
} else
|
||||
} else if (c == '\t')
|
||||
p->col += TAB_WIDTH;
|
||||
else
|
||||
p->col++;
|
||||
}
|
||||
|
||||
@@ -77,9 +81,9 @@ TokList lex(const char *s) {
|
||||
else if (streq_0_n("while", start, i))
|
||||
emit(&toks, &pos, (Tok){ .kind = TokWhile });
|
||||
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))
|
||||
emit(&toks, &pos, (Tok){ .kind = TokVal, .Val = { .type = { .kind = TypeBool, }, .Bool = false, }, });
|
||||
emit(&toks, &pos, (Tok){ .kind = TokVal, .Val = { .type = TypeBool, .Bool = false }});
|
||||
else {
|
||||
emit(&toks, &pos, (Tok){
|
||||
.kind = TokIdent,
|
||||
@@ -142,9 +146,7 @@ TokList lex(const char *s) {
|
||||
emit(&toks, &pos, (Tok){
|
||||
.kind = TokVal,
|
||||
.Val = {
|
||||
.type = {
|
||||
.kind = TypeFloat,
|
||||
},
|
||||
.type = TypeFloat,
|
||||
.Float = num,
|
||||
},
|
||||
});
|
||||
@@ -160,9 +162,7 @@ TokList lex(const char *s) {
|
||||
emit(&toks, &pos, (Tok){
|
||||
.kind = TokVal,
|
||||
.Val = {
|
||||
.type = {
|
||||
.kind = TypeInt,
|
||||
},
|
||||
.type = TypeInt,
|
||||
.Int = num,
|
||||
},
|
||||
});
|
||||
@@ -220,27 +220,39 @@ TokList lex(const char *s) {
|
||||
break;
|
||||
case '&':
|
||||
consume(&pos, *(s++));
|
||||
if (s[0] == '&') {
|
||||
if (s[0] == '&')
|
||||
emit(&toks, &pos, (Tok){ .kind = TokOp, .Op = OpAnd });
|
||||
} else
|
||||
else {
|
||||
emit(&toks, &pos, (Tok){ .kind = TokOp, .Op = OpAddrOf });
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
case '!':
|
||||
consume(&pos, *(s++));
|
||||
if (s[0] == '=')
|
||||
emit(&toks, &pos, (Tok){ .kind = TokOp, .Op = OpNeq });
|
||||
else {
|
||||
emit(&toks, &pos, (Tok){ .kind = TokOp, .Op = OpNot });
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
case '|':
|
||||
consume(&pos, *(s++));
|
||||
if (s[0] == '|') {
|
||||
if (s[0] == '|')
|
||||
emit(&toks, &pos, (Tok){ .kind = TokOp, .Op = OpOr });
|
||||
} else
|
||||
else
|
||||
continue;
|
||||
break;
|
||||
case '{':
|
||||
case '}':
|
||||
case '(':
|
||||
case ')':
|
||||
case '[':
|
||||
case ']':
|
||||
case ',':
|
||||
case '+':
|
||||
case '-':
|
||||
case '*':
|
||||
case '!':
|
||||
emit(&toks, &pos, (Tok){
|
||||
.kind = TokOp,
|
||||
.Op = s[0],
|
||||
@@ -287,7 +299,7 @@ TokList lex(const char *s) {
|
||||
consume(&pos, *(s++));
|
||||
c = get_esc_char(s[0]);
|
||||
if (!c) {
|
||||
set_err("Unrecognized escape sequence: '\\%c'", c);
|
||||
set_err("Unrecognized escape sequence: '\\%c'", s[0]);
|
||||
return toks;
|
||||
}
|
||||
}
|
||||
@@ -296,7 +308,55 @@ TokList lex(const char *s) {
|
||||
set_err("Unclosed char literal");
|
||||
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;
|
||||
}
|
||||
case '"': {
|
||||
consume(&pos, *(s++));
|
||||
const char *start = s;
|
||||
Pos start_pos = pos;
|
||||
size_t size = 0;
|
||||
|
||||
/* count the string size before allocating */
|
||||
while (s[0] != '"') {
|
||||
if (!s[0]) {
|
||||
set_err("Unexpected EOF in string literal");
|
||||
return toks;
|
||||
} else if (s[0] == '\\')
|
||||
consume(&pos, *(s++));
|
||||
consume(&pos, *(s++));
|
||||
size++;
|
||||
}
|
||||
|
||||
/* go through the actual string */
|
||||
s = start;
|
||||
pos = start_pos;
|
||||
char *str = xmalloc(size);
|
||||
for (size_t i = 0; i < size; i++) {
|
||||
char c = s[0];
|
||||
if (c == '\\') {
|
||||
consume(&pos, *(s++));
|
||||
c = get_esc_char(s[0]);
|
||||
if (!c) {
|
||||
set_err("Unrecognized escape sequence: '\\%c'", s[0]);
|
||||
free(str);
|
||||
return toks;
|
||||
}
|
||||
}
|
||||
consume(&pos, *(s++));
|
||||
str[i] = c;
|
||||
}
|
||||
emit(&toks, &pos, (Tok){ .kind = TokVal, .Val = {
|
||||
.type = TypeArr,
|
||||
.Arr = {
|
||||
.is_string = true,
|
||||
.dynamically_allocated = false,
|
||||
.type = TypeChar,
|
||||
.vals = str,
|
||||
.len = size,
|
||||
.cap = size,
|
||||
},
|
||||
},});
|
||||
break;
|
||||
}
|
||||
default:
|
||||
|
||||
191
main.c
191
main.c
@@ -8,6 +8,7 @@
|
||||
#include "ir.h"
|
||||
#include "lex.h"
|
||||
#include "parse.h"
|
||||
#include "runtime.h"
|
||||
#include "util.h"
|
||||
#include "vm.h"
|
||||
|
||||
@@ -33,34 +34,42 @@ static void die(const char *fmt, ...) {
|
||||
exit(1);
|
||||
}
|
||||
|
||||
static Value fn_put(Value *args) {
|
||||
switch (args[0].type.kind) {
|
||||
case TypeVoid: printf("(void)"); break;
|
||||
case TypeFloat: printf("%f", args[0].Float); break;
|
||||
case TypeInt: printf("%zd", args[0].Int); break;
|
||||
case TypeBool: printf("%s", args[0].Bool ? "true" : "false"); break;
|
||||
case TypeChar: printf("%c", args[0].Char); break;
|
||||
default:
|
||||
ASSERT_UNREACHED();
|
||||
static void fn_put(size_t extra_args, Value *args) {
|
||||
for (size_t i = 0;; i++) {
|
||||
print_value(&args[i], true);
|
||||
if (i+1 >= extra_args)
|
||||
break;
|
||||
printf(" ");
|
||||
}
|
||||
return (Value){0};
|
||||
}
|
||||
|
||||
static Value fn_print(Value *args) {
|
||||
fn_put(args);
|
||||
static void fn_putln(size_t extra_args, Value *args) {
|
||||
fn_put(extra_args, args);
|
||||
printf("\n");
|
||||
return (Value){0};
|
||||
}
|
||||
|
||||
static Value fn_int(Value *args) {
|
||||
Value ret = {
|
||||
.type.kind = TypeInt,
|
||||
.type = TypeInt,
|
||||
.Int = 0,
|
||||
};
|
||||
switch (args[0].type.kind) {
|
||||
case TypeVoid: break;
|
||||
switch (args[0].type) {
|
||||
case TypeVoid: break;
|
||||
case TypeFloat: ret.Int = (ssize_t)args[0].Float; break;
|
||||
case TypeInt: ret.Int = args[0].Int; break;
|
||||
case TypeBool: ret.Int = (ssize_t)args[0].Bool; break;
|
||||
case TypeChar: ret.Int = (ssize_t)args[0].Char; break;
|
||||
case TypeArr:
|
||||
if (args[0].Arr.is_string && args[0].Arr.type == TypeChar) {
|
||||
ssize_t endpos;
|
||||
ret.Int = stoimax((char*)args[0].Arr.vals, args[0].Arr.len, 10, &endpos);
|
||||
if (endpos != -1) {
|
||||
set_err("Error converting from string to int");
|
||||
return (Value){0};
|
||||
}
|
||||
} else
|
||||
ASSERT_UNREACHED();
|
||||
break;
|
||||
default: ASSERT_UNREACHED();
|
||||
}
|
||||
return ret;
|
||||
@@ -68,29 +77,138 @@ static Value fn_int(Value *args) {
|
||||
|
||||
static Value fn_float(Value *args) {
|
||||
Value ret = {
|
||||
.type.kind = TypeFloat,
|
||||
.type = TypeFloat,
|
||||
.Float = 0.0,
|
||||
};
|
||||
switch (args[0].type.kind) {
|
||||
case TypeVoid: break;
|
||||
case TypeFloat: ret.Float = args[0].Float; break;
|
||||
case TypeInt: ret.Float = (double)args[0].Int; break;
|
||||
switch (args[0].type) {
|
||||
case TypeVoid: break;
|
||||
case TypeFloat: ret.Float = args[0].Float; break;
|
||||
case TypeInt: ret.Float = (double)args[0].Int; break;
|
||||
case TypeBool: ret.Float = (double)args[0].Bool; break;
|
||||
case TypeChar: ret.Float = (double)args[0].Char; break;
|
||||
case TypeArr:
|
||||
if (args[0].Arr.is_string && args[0].Arr.type == TypeChar) {
|
||||
ssize_t endpos;
|
||||
ret.Float = stod((char*)args[0].Arr.vals, args[0].Arr.len, &endpos);
|
||||
if (endpos != -1) {
|
||||
set_err("Error converting from string to float");
|
||||
return (Value){0};
|
||||
}
|
||||
} else
|
||||
ASSERT_UNREACHED();
|
||||
break;
|
||||
default: ASSERT_UNREACHED();
|
||||
}
|
||||
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) {
|
||||
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");
|
||||
return (Value){0};
|
||||
}
|
||||
return (Value) {
|
||||
.type.kind = TypeFloat,
|
||||
return (Value){
|
||||
.type = TypeFloat,
|
||||
.Float = pow(args[0].Float, args[1].Float),
|
||||
};
|
||||
}
|
||||
|
||||
static void fn_sleep(Value *args) {
|
||||
if (!(args[0].type == TypeFloat && args[0].Float >= 0.0)) {
|
||||
set_err("sleep() requires a positive float");
|
||||
return;
|
||||
}
|
||||
sleep_secs(args[0].Float);
|
||||
}
|
||||
|
||||
static Value fn_getln(Value *args) {
|
||||
(void)args;
|
||||
|
||||
char *line = xmalloc(64);
|
||||
size_t len = 0, cap = 64;
|
||||
for (;;) {
|
||||
int c = fgetc(stdin);
|
||||
if (c == EOF)
|
||||
break;
|
||||
else if (c == '\n')
|
||||
break;
|
||||
if (len+1 > cap)
|
||||
line = xrealloc(line, (cap *= 2));
|
||||
line[len++] = c;
|
||||
}
|
||||
|
||||
return (Value){
|
||||
.type = TypeArr,
|
||||
.Arr = {
|
||||
.is_string = true,
|
||||
.dynamically_allocated = true,
|
||||
.type = TypeChar,
|
||||
.vals = line,
|
||||
.len = len,
|
||||
.cap = cap,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
int main(int argc, const char **argv) {
|
||||
/* parse arguments */
|
||||
size_t nargs = argc - 1;
|
||||
@@ -148,30 +266,37 @@ int main(int argc, const char **argv) {
|
||||
print_toks(&tokens);
|
||||
/* parse tokens into IR code */
|
||||
BuiltinFunc funcs[] = {
|
||||
{ .name = "put", .side_effects = true, .n_args = 1, .func = fn_put, },
|
||||
{ .name = "print", .side_effects = true, .n_args = 1, .func = fn_print, },
|
||||
{ .name = "int", .side_effects = false, .n_args = 1, .func = fn_int, },
|
||||
{ .name = "float", .side_effects = false, .n_args = 1, .func = fn_float, },
|
||||
{ .name = "pow", .side_effects = false, .n_args = 2, .func = fn_pow, },
|
||||
{ .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 = "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 = "bool", .kind = FuncFixedArgs, .returns = true, .side_effects = false, .FixedArgs = { .n_args = 1, .WithRet.func = fn_bool, }},
|
||||
{ .name = "char", .kind = FuncFixedArgs, .returns = true, .side_effects = false, .FixedArgs = { .n_args = 1, .WithRet.func = fn_char, }},
|
||||
{ .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) {
|
||||
irtoks_term(&ir);
|
||||
irlist_term(&ir);
|
||||
toklist_term(&tokens);
|
||||
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;
|
||||
}
|
||||
toklist_term(&tokens);
|
||||
optimize_ir(&ir);
|
||||
if (opt_emit_ir)
|
||||
print_ir(&ir, funcs);
|
||||
/* run the IR */
|
||||
if (!opt_dry) {
|
||||
run(&ir, funcs);
|
||||
if (err) {
|
||||
irtoks_term(&ir);
|
||||
irlist_term(&ir);
|
||||
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;
|
||||
}
|
||||
}
|
||||
irtoks_term(&ir);
|
||||
irlist_term(&ir);
|
||||
}
|
||||
|
||||
369
parse.c
369
parse.c
@@ -5,8 +5,6 @@
|
||||
#include "map.h"
|
||||
#include "runtime.h"
|
||||
|
||||
static BuiltinFunc *bf;
|
||||
|
||||
typedef struct Scope {
|
||||
struct Scope *parent;
|
||||
size_t mem_addr;
|
||||
@@ -32,10 +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 Scope make_scope(Scope *parent, bool with_idents);
|
||||
static void term_scope(Scope *sc);
|
||||
static ExprRet expr(IRToks *out_ir, TokList *toks, Map *funcs, Scope *parent_sc, TokListItem *t);
|
||||
static void expr_into_addr(IRToks *out_ir, TokList *toks, Map *funcs, Scope *parent_sc, TokListItem *t, size_t addr);
|
||||
static IRParam expr_into_irparam(IRToks *out_ir, TokList *toks, Map *funcs, Scope *parent_sc, TokListItem *t);
|
||||
static void stmt(IRToks *out_ir, TokList *toks, Map *funcs, Scope *sc, TokListItem *t);
|
||||
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 void make_value_statically_allocated(Value *v);
|
||||
static ExprRet expr(IRList *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 stmt(IRList *out_ir, TokList *toks, Map *funcs, Scope *sc, TokListItem *t);
|
||||
|
||||
static void mark_err(const Tok *t) {
|
||||
err_ln = t->ln;
|
||||
@@ -47,6 +48,7 @@ static void set_irtok_dest_addr(IRTok *t, size_t addr) {
|
||||
case IRSet:
|
||||
case IRNeg:
|
||||
case IRNot:
|
||||
case IRAddrOf:
|
||||
t->Unary.addr = addr;
|
||||
break;
|
||||
case IRAdd:
|
||||
@@ -54,6 +56,7 @@ static void set_irtok_dest_addr(IRTok *t, size_t addr) {
|
||||
case IRMul:
|
||||
case IRDiv:
|
||||
case IREq:
|
||||
case IRNeq:
|
||||
case IRLt:
|
||||
case IRLe:
|
||||
case IRAnd:
|
||||
@@ -63,6 +66,9 @@ static void set_irtok_dest_addr(IRTok *t, size_t addr) {
|
||||
case IRCallInternal:
|
||||
t->CallI.ret_addr = addr;
|
||||
break;
|
||||
case IRArrMake:
|
||||
t->ArrMake.arr_addr = addr;
|
||||
break;
|
||||
default:
|
||||
ASSERT_UNREACHED();
|
||||
}
|
||||
@@ -121,6 +127,50 @@ static void term_scope(Scope *sc) {
|
||||
map_term(&sc->ident_addrs);
|
||||
}
|
||||
|
||||
/* If ir_tok is the underlying expr() call's last evaluation, this function
|
||||
* deletes t from toks, sets *out_ret and tells the caller it can return
|
||||
* *out_ret by returning true.
|
||||
*
|
||||
* 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.
|
||||
* */
|
||||
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) {
|
||||
/* ir_tok was the expression's last IR instruction. */
|
||||
|
||||
toklist_del(toks, t, t);
|
||||
|
||||
*out_ret = (ExprRet){
|
||||
.kind = ExprRetLastInstr,
|
||||
.LastInstr = ir_tok,
|
||||
};
|
||||
return true;
|
||||
} else {
|
||||
/* ir_tok was not the expression's last IR instruction. */
|
||||
|
||||
size_t dest_addr = expr_scope->mem_addr++;
|
||||
|
||||
set_irtok_dest_addr(&ir_tok, dest_addr);
|
||||
irlist_app(out_ir, ir_tok);
|
||||
|
||||
t->tok = (Tok){
|
||||
.kind = TokIdent,
|
||||
.Ident = {
|
||||
.kind = IdentAddr,
|
||||
.Addr = dest_addr,
|
||||
},
|
||||
};
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
* writing the least IR instructions possible (without overanalyzing).
|
||||
* This means that the only IR instructions it will be writing are those for
|
||||
@@ -172,7 +222,7 @@ static void term_scope(Scope *sc) {
|
||||
* l_op r_op
|
||||
* 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;
|
||||
|
||||
Scope sc = make_scope(parent_sc, false);
|
||||
@@ -190,6 +240,10 @@ static ExprRet expr(IRToks *out_ir, TokList *toks, Map *funcs, Scope *parent_sc,
|
||||
t = t->next;
|
||||
perform_unary = true;
|
||||
unary_op = IRNot;
|
||||
} else if (t->tok.Op == OpAddrOf) {
|
||||
t = t->next;
|
||||
perform_unary = true;
|
||||
unary_op = IRAddrOf;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -208,7 +262,7 @@ static ExprRet expr(IRToks *out_ir, TokList *toks, Map *funcs, Scope *parent_sc,
|
||||
if (r.kind == ExprRetLastInstr) {
|
||||
size_t res_addr = sc.mem_addr++;
|
||||
set_irtok_dest_addr(&r.LastInstr, res_addr);
|
||||
irtoks_app(out_ir, r.LastInstr);
|
||||
irlist_app(out_ir, r.LastInstr);
|
||||
t->tok = (Tok){
|
||||
.ln = t->tok.ln,
|
||||
.col = t->tok.col,
|
||||
@@ -225,6 +279,7 @@ static ExprRet expr(IRToks *out_ir, TokList *toks, Map *funcs, Scope *parent_sc,
|
||||
ASSERT_UNREACHED();
|
||||
toklist_del(toks, t->next, t->next);
|
||||
}
|
||||
|
||||
/* Collapse function call. */
|
||||
else if (t->tok.kind == TokIdent && t->tok.Ident.kind == IdentName && t->next->tok.kind == TokOp && t->next->tok.Op == OpLParen) {
|
||||
/* get function */
|
||||
@@ -261,7 +316,7 @@ static ExprRet expr(IRToks *out_ir, TokList *toks, Map *funcs, Scope *parent_sc,
|
||||
eval_func_in_place = false;
|
||||
if (t->next->tok.kind == TokOp) {
|
||||
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;
|
||||
} else if (t->next->tok.Op == OpRParen) {
|
||||
toklist_del(toks, t->next, t->next); /* delete right parenthesis */
|
||||
@@ -278,10 +333,17 @@ static ExprRet expr(IRToks *out_ir, TokList *toks, Map *funcs, Scope *parent_sc,
|
||||
t = func_ident;
|
||||
toklist_del(toks, t->next, t->next); /* delete left parenthesis */
|
||||
|
||||
if (func.n_args != args_len) {
|
||||
if (func.kind == FuncFixedArgs && args_len != func.FixedArgs.n_args) {
|
||||
mark_err(&func_ident->tok);
|
||||
const char *plural = func.n_args == 1 ? "" : "s";
|
||||
set_err("Function %s() takes %zu argument%s but got %zu", func.name, func.n_args, plural, args_len);
|
||||
const char *plural = func.FixedArgs.n_args == 1 ? "" : "s";
|
||||
set_err("Function %s() takes %zu argument%s but got %zu", func.name, func.FixedArgs.n_args, plural, args_len);
|
||||
if (args)
|
||||
free(args);
|
||||
return (ExprRet){0};
|
||||
} else if (func.kind == FuncVarArgs && args_len < func.VarArgs.min_args) {
|
||||
mark_err(&func_ident->tok);
|
||||
const char *plural = func.VarArgs.min_args == 1 ? "" : "s";
|
||||
set_err("Function %s() requires at least %zu argument%s but only got %zu", func.name, func.VarArgs.min_args, plural, args_len);
|
||||
if (args)
|
||||
free(args);
|
||||
return (ExprRet){0};
|
||||
@@ -289,52 +351,143 @@ static ExprRet expr(IRToks *out_ir, TokList *toks, Map *funcs, Scope *parent_sc,
|
||||
|
||||
if (eval_func_in_place) {
|
||||
/* evaluate the function in place */
|
||||
if (!func.returns)
|
||||
/* If the function had no side effects and returned nothing,
|
||||
* that function would do absolutely nothing, which would
|
||||
* make no sense. */
|
||||
ASSERT_UNREACHED();
|
||||
Value *arg_vals = args_len ? xmalloc(sizeof(Value) * args_len) : NULL;
|
||||
for (size_t i = 0; i < args_len; i++)
|
||||
arg_vals[i] = args[i].Literal;
|
||||
mark_err(&func_ident->tok);
|
||||
func_ident->tok = (Tok) {
|
||||
.kind = TokVal,
|
||||
.Val = func.func(arg_vals),
|
||||
.Val = func.kind == FuncVarArgs ?
|
||||
func.VarArgs.WithRet.func(args_len - func.VarArgs.min_args, 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)
|
||||
free(arg_vals);
|
||||
if (args)
|
||||
free(args);
|
||||
} else {
|
||||
bool is_last_operation = t == start && t->next->tok.kind == TokOp && op_prec[t->next->tok.Op] == PREC_DELIM;
|
||||
|
||||
size_t res_addr = is_last_operation ? 0 : sc.mem_addr++;
|
||||
|
||||
/* function call instruction */
|
||||
IRTok ir = {
|
||||
/* function call IR instruction */
|
||||
IRTok ir_tok = {
|
||||
.ln = func_ident->tok.ln,
|
||||
.col = func_ident->tok.col,
|
||||
.instr = IRCallInternal,
|
||||
.CallI = {
|
||||
.ret_addr = res_addr,
|
||||
.ret_addr = 0,
|
||||
.fid = func.fid,
|
||||
.n_args = args_len,
|
||||
.args = args,
|
||||
},
|
||||
};
|
||||
|
||||
if (is_last_operation) {
|
||||
/* done */
|
||||
toklist_del(toks, t, t);
|
||||
return (ExprRet){ .kind = ExprRetLastInstr, .LastInstr = ir };
|
||||
} else {
|
||||
/* write IR instruction */
|
||||
irtoks_app(out_ir, ir);
|
||||
/* 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, func_ident, &ret))
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
/* leave new memory address as result */
|
||||
t->tok = (Tok){
|
||||
.kind = TokIdent,
|
||||
.Ident = {
|
||||
.kind = IdentAddr,
|
||||
.Addr = res_addr,
|
||||
},
|
||||
};
|
||||
/* 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. */
|
||||
@@ -343,43 +496,29 @@ static ExprRet expr(IRToks *out_ir, TokList *toks, Map *funcs, Scope *parent_sc,
|
||||
t = t->prev; /* go back to the '-' sign */
|
||||
toklist_del(toks, t->next, t->next); /* again, just removing the reference */
|
||||
|
||||
bool is_last_operation = t == start && t->next->tok.kind == TokOp && op_prec[t->next->tok.Op] == PREC_DELIM;
|
||||
|
||||
if (v->kind == TokVal) {
|
||||
/* immediately perform operation */
|
||||
t->tok.kind = TokVal;
|
||||
mark_err(&t->tok);
|
||||
TRY_RET(t->tok.Val = eval_unary(unary_op, &v->Val), (ExprRet){0});
|
||||
} else {
|
||||
size_t res_addr = is_last_operation ? 0 : sc.mem_addr++;
|
||||
|
||||
/* unary IR instruction */
|
||||
IRParam v_irparam;
|
||||
TRY_RET(v_irparam = tok_to_irparam(&sc, v), (ExprRet){0});
|
||||
IRTok ir = {
|
||||
IRTok ir_tok = {
|
||||
.ln = t->tok.ln,
|
||||
.col = t->tok.col,
|
||||
.instr = unary_op,
|
||||
.Unary = {
|
||||
.addr = res_addr,
|
||||
.addr = 0,
|
||||
.val = v_irparam,
|
||||
},
|
||||
};
|
||||
|
||||
if (is_last_operation) {
|
||||
/* done */
|
||||
toklist_del(toks, t, t);
|
||||
return (ExprRet){ .kind = ExprRetLastInstr, .LastInstr = ir };
|
||||
} else {
|
||||
/* write IR instruction */
|
||||
irtoks_app(out_ir, ir);
|
||||
|
||||
/* leave new memory address as result */
|
||||
t->tok.kind = TokIdent;
|
||||
t->tok.Ident = (Identifier){
|
||||
.kind = IdentAddr,
|
||||
.Addr = res_addr,
|
||||
};
|
||||
}
|
||||
/* 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, t, &ret))
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -455,6 +594,7 @@ static ExprRet expr(IRToks *out_ir, TokList *toks, Map *funcs, Scope *parent_sc,
|
||||
case OpMul: instr = IRMul; break;
|
||||
case OpDiv: instr = IRDiv; break;
|
||||
case OpEq: instr = IREq; break;
|
||||
case OpNeq: instr = IRNeq; break;
|
||||
case OpLt: instr = IRLt; break;
|
||||
case OpLe: instr = IRLe; break;
|
||||
case OpGt: instr = IRLt; swap_operands = true; break;
|
||||
@@ -472,53 +612,40 @@ static ExprRet expr(IRToks *out_ir, TokList *toks, Map *funcs, Scope *parent_sc,
|
||||
Value *lhs_val = swap_operands ? &rhs->Val : &lhs->Val;
|
||||
Value *rhs_val = swap_operands ? &lhs->Val : &rhs->Val;
|
||||
lhs->kind = TokVal;
|
||||
mark_err(l_op);
|
||||
TRY_RET(lhs->Val = eval_binary(instr, lhs_val, rhs_val), (ExprRet){0});
|
||||
} else {
|
||||
bool is_last_operation = t == start && r_op_prec == PREC_DELIM;
|
||||
|
||||
IRParam lhs_irparam, rhs_irparam;
|
||||
TRY_RET(lhs_irparam = tok_to_irparam(&sc, lhs), (ExprRet){0});
|
||||
TRY_RET(rhs_irparam = tok_to_irparam(&sc, rhs), (ExprRet){0});
|
||||
|
||||
size_t res_addr = is_last_operation ? 0 : sc.mem_addr++;
|
||||
|
||||
IRTok ir = {
|
||||
/* binary IR instruction */
|
||||
IRTok ir_tok = {
|
||||
.ln = l_op->ln,
|
||||
.col = l_op->col,
|
||||
.instr = instr,
|
||||
.Binary = {
|
||||
.addr = res_addr,
|
||||
.addr = 0,
|
||||
.lhs = swap_operands ? rhs_irparam : lhs_irparam,
|
||||
.rhs = swap_operands ? lhs_irparam : rhs_irparam,
|
||||
},
|
||||
};
|
||||
|
||||
if (is_last_operation) {
|
||||
/* done */
|
||||
toklist_del(toks, t, t);
|
||||
return (ExprRet){ .kind = ExprRetLastInstr, .LastInstr = ir };
|
||||
} else {
|
||||
/* write IR instruction */
|
||||
irtoks_app(out_ir, ir);
|
||||
|
||||
/* leave new memory address as result */
|
||||
lhs->kind = TokIdent;
|
||||
lhs->Ident = (Identifier){
|
||||
.kind = IdentAddr,
|
||||
.Addr = res_addr,
|
||||
};
|
||||
}
|
||||
/* 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, t, &ret))
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
TRY(r = expr(out_ir, toks, funcs, parent_sc, t));
|
||||
if (r.kind == ExprRetLastInstr) {
|
||||
set_irtok_dest_addr(&r.LastInstr, addr);
|
||||
irtoks_app(out_ir, r.LastInstr);
|
||||
irlist_app(out_ir, r.LastInstr);
|
||||
t->tok = (Tok){
|
||||
.ln = t->tok.ln,
|
||||
.col = t->tok.col,
|
||||
@@ -531,7 +658,9 @@ static void expr_into_addr(IRToks *out_ir, TokList *toks, Map *funcs, Scope *par
|
||||
} else if (r.kind == ExprRetVal || r.kind == ExprRetIdent) {
|
||||
IRParam res;
|
||||
TRY(res = tok_to_irparam(parent_sc, &t->tok));
|
||||
irtoks_app(out_ir, (IRTok){
|
||||
irlist_app(out_ir, (IRTok){
|
||||
.ln = t->tok.ln,
|
||||
.col = t->tok.col,
|
||||
.instr = IRSet,
|
||||
.Unary = {
|
||||
.addr = addr,
|
||||
@@ -543,14 +672,14 @@ static void expr_into_addr(IRToks *out_ir, TokList *toks, Map *funcs, Scope *par
|
||||
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;
|
||||
TRY_RET(r = expr(out_ir, toks, funcs, parent_sc, t), (IRParam){0});
|
||||
if (r.kind == ExprRetLastInstr) {
|
||||
Scope sc = make_scope(parent_sc, false);
|
||||
size_t addr = sc.mem_addr++;
|
||||
set_irtok_dest_addr(&r.LastInstr, addr);
|
||||
irtoks_app(out_ir, r.LastInstr);
|
||||
irlist_app(out_ir, r.LastInstr);
|
||||
return (IRParam){
|
||||
.kind = IRParamAddr,
|
||||
.Addr = addr,
|
||||
@@ -564,20 +693,30 @@ static IRParam expr_into_irparam(IRToks *out_ir, TokList *toks, Map *funcs, Scop
|
||||
ASSERT_UNREACHED();
|
||||
}
|
||||
|
||||
static void stmt(IRToks *out_ir, TokList *toks, Map *funcs, Scope *sc, TokListItem *t) {
|
||||
/* This WILL invalidate *from, so the caller should only call it on a
|
||||
* TokListItem after any ones that are in use (e.g. skip_newlns(t->next)). */
|
||||
static void skip_newlns(TokList *toks, TokListItem *from) {
|
||||
TokListItem *curr = from;
|
||||
while (curr->tok.kind == TokOp && curr->tok.Op == OpNewLn)
|
||||
curr = curr->next;
|
||||
if (curr != from)
|
||||
toklist_del(toks, from, curr->prev);
|
||||
}
|
||||
|
||||
static void stmt(IRList *out_ir, TokList *toks, Map *funcs, Scope *sc, TokListItem *t) {
|
||||
TokListItem *start = t;
|
||||
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;
|
||||
t = t->next;
|
||||
if (t->tok.kind == TokDeclare) {
|
||||
size_t addr = sc->mem_addr++;
|
||||
TRY(expr_into_addr(out_ir, toks, funcs, sc, t->next, addr));
|
||||
bool replaced = map_insert(&sc->ident_addrs, name, &addr);
|
||||
if (replaced) {
|
||||
mark_err(&start->tok);
|
||||
set_err("'%s' already declared in this scope", name);
|
||||
return;
|
||||
}
|
||||
TRY(expr_into_addr(out_ir, toks, funcs, sc, t->next, addr));
|
||||
} else if (t->tok.kind == TokAssign) {
|
||||
size_t addr;
|
||||
TRY(addr = get_ident_addr(sc, name, &start->tok));
|
||||
@@ -587,11 +726,12 @@ static void stmt(IRToks *out_ir, TokList *toks, Map *funcs, Scope *sc, TokListIt
|
||||
} else if (t->tok.kind == TokOp && t->tok.Op == OpLCurl) {
|
||||
Scope inner_sc = make_scope(sc, true);
|
||||
for (;;) {
|
||||
skip_newlns(toks, t->next);
|
||||
if (t->next->tok.kind == TokOp) {
|
||||
if (t->next->tok.Op == OpEOF) {
|
||||
term_scope(&inner_sc);
|
||||
mark_err(&start->tok);
|
||||
set_err("Unclosed '{'");
|
||||
term_scope(&inner_sc);
|
||||
return;
|
||||
}
|
||||
if (t->next->tok.Op == OpRCurl)
|
||||
@@ -611,8 +751,7 @@ static void stmt(IRToks *out_ir, TokList *toks, Map *funcs, Scope *sc, TokListIt
|
||||
* */
|
||||
|
||||
/* add initial jmp instruction */
|
||||
size_t jmp_instr_iaddr = out_ir->len;
|
||||
irtoks_app(out_ir, (IRTok){
|
||||
irlist_app(out_ir, (IRTok){
|
||||
.ln = t->tok.ln,
|
||||
.col = t->tok.col,
|
||||
.instr = IRJmp,
|
||||
@@ -620,29 +759,32 @@ static void stmt(IRToks *out_ir, TokList *toks, Map *funcs, Scope *sc, TokListIt
|
||||
.iaddr = 0, /* unknown for now */
|
||||
},
|
||||
});
|
||||
IRItem *jmp_instr = out_ir->end;
|
||||
size_t body_iaddr = out_ir->len;
|
||||
|
||||
/* parse condition */
|
||||
IRToks cond_ir;
|
||||
irtoks_init_short(&cond_ir);
|
||||
IRList cond_ir;
|
||||
irlist_init_short(&cond_ir);
|
||||
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 */
|
||||
TRY_ELSE(stmt(out_ir, toks, funcs, sc, t->next), irtoks_term(&cond_ir));
|
||||
skip_newlns(toks, t->next);
|
||||
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 */
|
||||
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 */
|
||||
irtoks_eat_irtoks(out_ir, &cond_ir, out_ir->len-1);
|
||||
irlist_eat_irlist(out_ir, &cond_ir);
|
||||
|
||||
/* add conditional jump */
|
||||
irtoks_app(out_ir, (IRTok){
|
||||
irlist_app(out_ir, (IRTok){
|
||||
.ln = t->next->tok.ln,
|
||||
.col = t->next->tok.col,
|
||||
.instr = IRJnz,
|
||||
.CJmp = {
|
||||
.iaddr = jmp_instr_iaddr + 1,
|
||||
.iaddr = body_iaddr,
|
||||
.condition = cond,
|
||||
},
|
||||
});
|
||||
@@ -662,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));
|
||||
|
||||
/* add conditional jmp instruction */
|
||||
size_t if_cjmp_instr_iaddr = out_ir->len;
|
||||
irtoks_app(out_ir, (IRTok){
|
||||
irlist_app(out_ir, (IRTok){
|
||||
.ln = t->tok.ln,
|
||||
.col = t->tok.col,
|
||||
.instr = IRJnz,
|
||||
@@ -672,22 +813,25 @@ static void stmt(IRToks *out_ir, TokList *toks, Map *funcs, Scope *sc, TokListIt
|
||||
.condition = cond,
|
||||
},
|
||||
});
|
||||
IRItem *if_cjmp_instr = out_ir->end;
|
||||
|
||||
/* parse if body */
|
||||
IRToks if_body;
|
||||
irtoks_init_short(&if_body);
|
||||
TRY_ELSE(stmt(&if_body, toks, funcs, sc, t->next), irtoks_term(&if_body));
|
||||
skip_newlns(toks, t->next);
|
||||
IRList if_body;
|
||||
irlist_init_short(&if_body);
|
||||
TRY_ELSE(stmt(&if_body, toks, funcs, sc, t->next), irlist_term(&if_body));
|
||||
|
||||
skip_newlns(toks, t->next);
|
||||
if (t->next->tok.kind == TokElse) {
|
||||
toklist_del(toks, t->next, t->next);
|
||||
|
||||
/* parse and add else body */
|
||||
TRY_ELSE(stmt(out_ir, toks, funcs, sc, t->next), irtoks_term(&if_body));
|
||||
skip_newlns(toks, t->next);
|
||||
TRY_ELSE(stmt(out_ir, toks, funcs, sc, t->next), irlist_term(&if_body));
|
||||
}
|
||||
|
||||
/* add jmp instruction to jump back to common code */
|
||||
size_t else_jmp_instr_iaddr = out_ir->len;
|
||||
irtoks_app(out_ir, (IRTok){
|
||||
irlist_app(out_ir, (IRTok){
|
||||
.ln = t->tok.ln,
|
||||
.col = t->tok.col,
|
||||
.instr = IRJmp,
|
||||
@@ -695,16 +839,16 @@ static void stmt(IRToks *out_ir, TokList *toks, Map *funcs, Scope *sc, TokListIt
|
||||
.iaddr = 0, /* unknown for now */
|
||||
},
|
||||
});
|
||||
IRItem *else_jmp_instr = out_ir->end;
|
||||
|
||||
/* 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 */
|
||||
irtoks_eat_irtoks(out_ir, &if_body, out_ir->len-1);
|
||||
irlist_eat_irlist(out_ir, &if_body);
|
||||
|
||||
/* set else jmp target */
|
||||
out_ir->toks[else_jmp_instr_iaddr].CJmp.iaddr = out_ir->len;
|
||||
} else if (t->tok.kind == TokOp && t->tok.Op == OpNewLn) {
|
||||
else_jmp_instr->tok.CJmp.iaddr = out_ir->len;
|
||||
} else {
|
||||
/* assume expression */
|
||||
TRY(expr_into_irparam(out_ir, toks, funcs, sc, t));
|
||||
@@ -713,9 +857,7 @@ static void stmt(IRToks *out_ir, TokList *toks, Map *funcs, Scope *sc, TokListIt
|
||||
toklist_del(toks, start, t);
|
||||
}
|
||||
|
||||
IRToks parse(TokList *toks, BuiltinFunc *builtin_funcs, size_t n_builtin_funcs) {
|
||||
bf = builtin_funcs;
|
||||
|
||||
IRList parse(TokList *toks, BuiltinFunc *builtin_funcs, size_t n_builtin_funcs) {
|
||||
Map funcs;
|
||||
map_init(&funcs, sizeof(BuiltinFunc));
|
||||
for (size_t i = 0; i < n_builtin_funcs; i++) {
|
||||
@@ -725,14 +867,15 @@ IRToks parse(TokList *toks, BuiltinFunc *builtin_funcs, size_t n_builtin_funcs)
|
||||
err_ln = 0; err_col = 0;
|
||||
set_err("Builtin function %s() declared more than once", builtin_funcs[i].name);
|
||||
map_term(&funcs);
|
||||
return (IRToks){0};
|
||||
return (IRList){0};
|
||||
}
|
||||
}
|
||||
|
||||
IRToks ir;
|
||||
irtoks_init_long(&ir);
|
||||
IRList ir;
|
||||
irlist_init_long(&ir);
|
||||
Scope global_scope = make_scope(NULL, true);
|
||||
for (;;) {
|
||||
skip_newlns(toks, toks->begin);
|
||||
if (toks->begin->tok.kind == TokOp && toks->begin->tok.Op == OpEOF)
|
||||
break;
|
||||
TRY_RET_ELSE(stmt(&ir, toks, &funcs, &global_scope, toks->begin), ir,
|
||||
|
||||
2
parse.h
2
parse.h
@@ -5,6 +5,6 @@
|
||||
#include "tok.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 */
|
||||
|
||||
85
runtime.c
85
runtime.c
@@ -8,7 +8,7 @@ Value eval_binary(IRInstr instr, const Value *lhs, const Value *rhs) {
|
||||
case IRSub:
|
||||
case IRMul:
|
||||
case IRDiv: {
|
||||
if (lhs->type.kind == TypeInt && rhs->type.kind == TypeInt) {
|
||||
if (lhs->type == TypeInt && rhs->type == TypeInt) {
|
||||
ssize_t res;
|
||||
switch (instr) {
|
||||
case IRAdd: res = lhs->Int + rhs->Int; break;
|
||||
@@ -18,11 +18,11 @@ Value eval_binary(IRInstr instr, const Value *lhs, const Value *rhs) {
|
||||
default: ASSERT_UNREACHED();
|
||||
}
|
||||
return (Value){
|
||||
.type.kind = TypeInt,
|
||||
.type = TypeInt,
|
||||
.Int = res,
|
||||
};
|
||||
} else if (lhs->type.kind == TypeFloat && rhs->type.kind == TypeFloat) {
|
||||
float res;
|
||||
} else if (lhs->type == TypeFloat && rhs->type == TypeFloat) {
|
||||
double res;
|
||||
switch (instr) {
|
||||
case IRAdd: res = lhs->Float + rhs->Float; break;
|
||||
case IRSub: res = lhs->Float - rhs->Float; break;
|
||||
@@ -31,49 +31,66 @@ Value eval_binary(IRInstr instr, const Value *lhs, const Value *rhs) {
|
||||
default: ASSERT_UNREACHED();
|
||||
}
|
||||
return (Value){
|
||||
.type.kind = TypeFloat,
|
||||
.type = TypeFloat,
|
||||
.Float = res,
|
||||
};
|
||||
} else {
|
||||
set_err("Unsupported types for operation '%s'", irinstr_str[instr]);
|
||||
set_err("Unsupported types for operation '%s': %s and %s", irinstr_str[instr], type_str[lhs->type], type_str[rhs->type]);
|
||||
return (Value){0};
|
||||
}
|
||||
}
|
||||
case IREq:
|
||||
case IRNeq:
|
||||
case IRLt:
|
||||
case IRLe:
|
||||
case IRLe: {
|
||||
bool res;
|
||||
if (lhs->type.kind == TypeInt && rhs->type.kind == TypeInt) {
|
||||
if (lhs->type == TypeInt && rhs->type == TypeInt) {
|
||||
switch (instr) {
|
||||
case IREq: res = lhs->Int == rhs->Int; break;
|
||||
case IRLt: res = lhs->Int < rhs->Int; break;
|
||||
case IRLe: res = lhs->Int <= rhs->Int; break;
|
||||
case IREq: res = lhs->Int == rhs->Int; break;
|
||||
case IRNeq: res = lhs->Int != rhs->Int; break;
|
||||
case IRLt: res = lhs->Int < rhs->Int; break;
|
||||
case IRLe: res = lhs->Int <= rhs->Int; break;
|
||||
default: ASSERT_UNREACHED();
|
||||
};
|
||||
} else if (lhs->type.kind == TypeFloat && rhs->type.kind == TypeFloat) {
|
||||
} else if (lhs->type == TypeFloat && rhs->type == TypeFloat) {
|
||||
switch (instr) {
|
||||
case IREq: res = lhs->Float == rhs->Float; break;
|
||||
case IRLt: res = lhs->Float < rhs->Float; break;
|
||||
case IRLe: res = lhs->Float <= rhs->Float; break;
|
||||
case IREq: res = lhs->Float == rhs->Float; break;
|
||||
case IRNeq: res = lhs->Float != rhs->Float; break;
|
||||
case IRLt: res = lhs->Float < rhs->Float; break;
|
||||
case IRLe: res = lhs->Float <= rhs->Float; break;
|
||||
default: ASSERT_UNREACHED();
|
||||
};
|
||||
} else if (lhs->type == TypeArr && lhs->Arr.type == TypeChar && lhs->Arr.is_string &&
|
||||
rhs->type == TypeArr && rhs->Arr.type == TypeChar && rhs->Arr.is_string) {
|
||||
switch (instr) {
|
||||
case IREq:
|
||||
res = lhs->Arr.len == rhs->Arr.len ? strncmp(lhs->Arr.vals, rhs->Arr.vals, lhs->Arr.len) == 0 : false;
|
||||
break;
|
||||
case IRNeq:
|
||||
res = lhs->Arr.len == rhs->Arr.len ? strncmp(lhs->Arr.vals, rhs->Arr.vals, lhs->Arr.len) != 0 : true;
|
||||
break;
|
||||
default:
|
||||
set_err("String operation '%s' not supported", irinstr_str[instr]);
|
||||
break;
|
||||
};
|
||||
} else {
|
||||
set_err("Unsupported types for operation '%s'", irinstr_str[instr]);
|
||||
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){
|
||||
.type.kind = TypeBool,
|
||||
.type = TypeBool,
|
||||
.Bool = res,
|
||||
};
|
||||
}
|
||||
case IRAnd:
|
||||
return (Value){
|
||||
.type.kind = TypeBool,
|
||||
.type = TypeBool,
|
||||
.Bool = is_nonzero(lhs) && is_nonzero(rhs),
|
||||
};
|
||||
break;
|
||||
case IROr:
|
||||
return (Value){
|
||||
.type.kind = TypeBool,
|
||||
.type = TypeBool,
|
||||
.Bool = is_nonzero(lhs) || is_nonzero(rhs),
|
||||
};
|
||||
break;
|
||||
@@ -88,31 +105,37 @@ Value eval_unary(IRInstr instr, const Value *v) {
|
||||
case IRSet:
|
||||
return *v;
|
||||
case IRNeg:
|
||||
if (v->type.kind == TypeInt)
|
||||
return (Value){ .type.kind = TypeInt, .Int = -v->Int };
|
||||
else if (v->type.kind == TypeFloat)
|
||||
return (Value){ .type.kind = TypeFloat, .Float = -v->Float };
|
||||
if (v->type == TypeInt)
|
||||
return (Value){ .type = TypeInt, .Int = -v->Int };
|
||||
else if (v->type == TypeFloat)
|
||||
return (Value){ .type = TypeFloat, .Float = -v->Float };
|
||||
else {
|
||||
set_err("Unsupported type for operation '%s'", irinstr_str[instr]);
|
||||
set_err("Unsupported type for operation '%s': %s", irinstr_str[instr], type_str[v->type]);
|
||||
return (Value){0};
|
||||
}
|
||||
case IRNot:
|
||||
if (v->type.kind == TypeBool) {
|
||||
return (Value){ .type.kind = TypeBool, .Bool = !v->Bool };
|
||||
if (v->type == TypeBool) {
|
||||
return (Value){ .type = TypeBool, .Bool = !v->Bool };
|
||||
} else {
|
||||
set_err("Unsupported type for operation '%s'", irinstr_str[instr]);
|
||||
set_err("Unsupported type for operation '%s': %s", irinstr_str[instr], type_str[v->type]);
|
||||
return (Value){0};
|
||||
}
|
||||
case IRAddrOf:
|
||||
set_err("Unable to take the address of a literal");
|
||||
return (Value){0};
|
||||
default:
|
||||
ASSERT_UNREACHED();
|
||||
}
|
||||
}
|
||||
|
||||
bool is_nonzero(const Value *v) {
|
||||
switch (v->type.kind) {
|
||||
case TypeInt: return v->Int != 0;
|
||||
case TypeFloat: return v->Float != 0.0;
|
||||
switch (v->type) {
|
||||
case TypeInt: return v->Int != 0;
|
||||
case TypeFloat: return v->Float != 0.0;
|
||||
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();
|
||||
}
|
||||
}
|
||||
@@ -120,7 +143,7 @@ bool is_nonzero(const Value *v) {
|
||||
Value zero_val(Type ty) {
|
||||
Value ret;
|
||||
ret.type = ty;
|
||||
switch (ty.kind) {
|
||||
switch (ty) {
|
||||
case TypeInt: ret.Int = 0; break;
|
||||
case TypeFloat: ret.Float = 0.0; break;
|
||||
case TypeBool: ret.Bool = false; break;
|
||||
|
||||
133
tok.c
133
tok.c
@@ -5,15 +5,124 @@
|
||||
|
||||
#include "util.h"
|
||||
|
||||
size_t type_size[TypeEnumSize] = {
|
||||
[TypeVoid] = 0,
|
||||
[TypeFloat] = sizeof(((Value*)NULL)->Float),
|
||||
[TypeInt] = sizeof(((Value*)NULL)->Int),
|
||||
[TypeBool] = sizeof(((Value*)NULL)->Bool),
|
||||
[TypeChar] = sizeof(((Value*)NULL)->Char),
|
||||
[TypePtr] = sizeof(((Value*)NULL)->Ptr),
|
||||
[TypeArr] = sizeof(((Value*)NULL)->Arr),
|
||||
};
|
||||
|
||||
const char *type_str[TypeEnumSize] = {
|
||||
[TypeVoid] = "void",
|
||||
[TypeFloat] = "float",
|
||||
[TypeInt] = "int",
|
||||
[TypeBool] = "bool",
|
||||
[TypeChar] = "char",
|
||||
[TypePtr] = "ptr",
|
||||
[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) {
|
||||
switch (v->type) {
|
||||
case TypeVoid:
|
||||
printf("(void)");
|
||||
break;
|
||||
case TypeFloat:
|
||||
printf("%f", v->Float);
|
||||
break;
|
||||
case TypeInt:
|
||||
printf("%zd", v->Int);
|
||||
break;
|
||||
case TypeBool:
|
||||
printf("%s", v->Bool ? "true" : "false");
|
||||
break;
|
||||
case TypeChar:
|
||||
if (raw)
|
||||
printf("%c", v->Char);
|
||||
else {
|
||||
const char *esc = unescape_char(v->Char);
|
||||
if (esc) printf("'%s'", esc);
|
||||
else printf("'%c'", v->Char);
|
||||
}
|
||||
break;
|
||||
case TypePtr: {
|
||||
if (v->Ptr.val) {
|
||||
printf("ptr<%s>(", type_str[v->Ptr.type]);
|
||||
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;
|
||||
}
|
||||
case TypeArr:
|
||||
if (v->Arr.is_string) {
|
||||
if (v->Arr.type != TypeChar)
|
||||
ASSERT_UNREACHED();
|
||||
char *str = v->Arr.vals;
|
||||
if (!raw)
|
||||
printf("\"");
|
||||
for (size_t i = 0; i < v->Arr.len; i++) {
|
||||
char c = str[i];
|
||||
if (raw)
|
||||
printf("%c", c);
|
||||
else {
|
||||
const char *esc = unescape_char(c);
|
||||
if (esc) printf("%s", esc);
|
||||
else printf("%c", c);
|
||||
}
|
||||
}
|
||||
if (!raw)
|
||||
printf("\"");
|
||||
} else {
|
||||
printf("[");
|
||||
for (size_t i = 0;; i++) {
|
||||
size_t ty_sz = type_size[v->Arr.type];
|
||||
Value ty_val = { .type = v->Arr.type };
|
||||
memcpy(&ty_val.Void, (uint8_t*)v->Arr.vals + ty_sz * i, ty_sz);
|
||||
print_value(&ty_val, false);
|
||||
if (i+1 >= v->Arr.len) break;
|
||||
printf(", ");
|
||||
}
|
||||
printf("]");
|
||||
}
|
||||
break;
|
||||
default:
|
||||
ASSERT_UNREACHED();
|
||||
}
|
||||
}
|
||||
|
||||
int8_t op_prec[OperatorEnumSize] = {
|
||||
[OpEOF] = PREC_DELIM,
|
||||
[OpNewLn] = PREC_DELIM,
|
||||
[OpLCurl] = PREC_DELIM,
|
||||
[OpRParen] = PREC_DELIM,
|
||||
[OpRBrack] = PREC_DELIM,
|
||||
[OpComma] = PREC_DELIM,
|
||||
[OpAnd] = 0,
|
||||
[OpOr] = 0,
|
||||
[OpEq] = 1,
|
||||
[OpNeq] = 1,
|
||||
[OpLt] = 1,
|
||||
[OpGt] = 1,
|
||||
[OpLe] = 1,
|
||||
@@ -29,6 +138,8 @@ const char *op_str[OperatorEnumSize] = {
|
||||
[OpRCurl] = "}",
|
||||
[OpLParen] = "(",
|
||||
[OpRParen] = ")",
|
||||
[OpLBrack] = "[",
|
||||
[OpRBrack] = "]",
|
||||
[OpComma] = ",",
|
||||
[OpAdd] = "+",
|
||||
[OpSub] = "-",
|
||||
@@ -38,6 +149,7 @@ const char *op_str[OperatorEnumSize] = {
|
||||
[OpNewLn] = "\\n",
|
||||
[OpEOF] = "EOF",
|
||||
[OpEq] = "==",
|
||||
[OpNeq] = "!=",
|
||||
[OpLt] = "<",
|
||||
[OpGt] = ">",
|
||||
[OpLe] = "<=",
|
||||
@@ -108,24 +220,9 @@ void print_toks(TokList *l) {
|
||||
printf(": " C_ICYAN "%s" C_RESET, op_str[i->tok.Op]);
|
||||
break;
|
||||
case TokVal:
|
||||
printf(C_IYELLOW "Val" C_RESET);
|
||||
switch (i->tok.Val.type.kind) {
|
||||
case TypeFloat:
|
||||
printf(": " C_ICYAN "%f" C_RESET, i->tok.Val.Float);
|
||||
break;
|
||||
case TypeInt:
|
||||
printf(": " C_ICYAN "%zd" C_RESET, i->tok.Val.Int);
|
||||
break;
|
||||
case TypeBool:
|
||||
printf(": " C_ICYAN "%s" C_RESET, i->tok.Val.Bool ? "true" : "false");
|
||||
break;
|
||||
case TypeChar:
|
||||
printf(": " C_ICYAN "'%c'" C_RESET, i->tok.Val.Char);
|
||||
break;
|
||||
default:
|
||||
printf(" " C_ICYAN "(unknown type)" C_RESET);
|
||||
break;
|
||||
}
|
||||
printf(C_IYELLOW "Val" C_RESET ": " C_ICYAN);
|
||||
print_value(&i->tok.Val, false);
|
||||
printf(C_RESET);
|
||||
break;
|
||||
case TokIdent:
|
||||
printf(C_IYELLOW "Ident" C_RESET);
|
||||
|
||||
44
tok.h
44
tok.h
@@ -2,47 +2,67 @@
|
||||
#define __TOK_H__
|
||||
|
||||
#include <stdint.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "util.h"
|
||||
|
||||
typedef struct Type {
|
||||
enum {
|
||||
TypeVoid = 0,
|
||||
TypeFloat,
|
||||
TypeInt,
|
||||
TypeBool,
|
||||
TypeChar,
|
||||
} kind;
|
||||
enum Type {
|
||||
TypeVoid = 0,
|
||||
TypeFloat,
|
||||
TypeInt,
|
||||
TypeBool,
|
||||
TypeChar,
|
||||
TypePtr,
|
||||
TypeArr,
|
||||
TypeEnumSize,
|
||||
};
|
||||
typedef enum Type Type;
|
||||
|
||||
/*union {
|
||||
};*/
|
||||
} Type;
|
||||
extern size_t type_size[TypeEnumSize];
|
||||
extern const char *type_str[TypeEnumSize];
|
||||
|
||||
typedef struct Value {
|
||||
Type type;
|
||||
|
||||
union {
|
||||
pseudo_void Void;
|
||||
double Float;
|
||||
ssize_t Int;
|
||||
bool Bool;
|
||||
char Char;
|
||||
struct {
|
||||
Type type;
|
||||
void *val;
|
||||
} Ptr;
|
||||
struct {
|
||||
Type type;
|
||||
bool is_string : 1;
|
||||
bool dynamically_allocated : 1;
|
||||
void *vals;
|
||||
size_t len, cap;
|
||||
} Arr;
|
||||
};
|
||||
} Value;
|
||||
|
||||
void free_value(Value *v, bool purge);
|
||||
void print_value(const Value *v, bool raw);
|
||||
|
||||
enum Operator {
|
||||
OpLCurl = '{',
|
||||
OpRCurl = '}',
|
||||
OpLParen = '(',
|
||||
OpRParen = ')',
|
||||
OpLBrack = '[',
|
||||
OpRBrack = ']',
|
||||
OpComma = ',',
|
||||
OpAdd = '+',
|
||||
OpSub = '-',
|
||||
OpMul = '*',
|
||||
OpDiv = '/',
|
||||
OpNot = '!',
|
||||
OpAddrOf = '&',
|
||||
OpBeginNonchars = 256,
|
||||
OpEq,
|
||||
OpNeq,
|
||||
OpLt,
|
||||
OpGt,
|
||||
OpLe,
|
||||
|
||||
34
util.c
34
util.c
@@ -2,6 +2,23 @@
|
||||
|
||||
#include <stdarg.h>
|
||||
|
||||
#ifdef WIN32
|
||||
#include <windows.h> /* Sleep */
|
||||
#else
|
||||
#include <time.h> /* nanosleep */
|
||||
#endif
|
||||
|
||||
void sleep_secs(double secs) {
|
||||
#if defined(_WIN32) || defined(WIN32)
|
||||
Sleep(secs * 1000.0);
|
||||
#else
|
||||
struct timespec ts;
|
||||
ts.tv_sec = (time_t)secs;
|
||||
ts.tv_nsec = (secs - (double)ts.tv_sec) * 1000000000.0;
|
||||
nanosleep(&ts, NULL);
|
||||
#endif
|
||||
}
|
||||
|
||||
char errbuf[ERRSZ];
|
||||
bool err;
|
||||
size_t err_ln, err_col;
|
||||
@@ -168,6 +185,23 @@ double stod(const char *s, size_t n, ssize_t *endpos) {
|
||||
return res;
|
||||
}
|
||||
|
||||
const char *unescape_char(char c) {
|
||||
switch (c) {
|
||||
case '\a': return "\\a";
|
||||
case '\b': return "\\b";
|
||||
case '\033': return "\\e";
|
||||
case '\f': return "\\f";
|
||||
case '\n': return "\\n";
|
||||
case '\r': return "\\r";
|
||||
case '\t': return "\\t";
|
||||
case '\v': return "\\v";
|
||||
case '\\': return "\\\\";
|
||||
case '\'': return "\\'";
|
||||
case '"': return "\\\"";
|
||||
default: return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
char *mreadfile(FILE *fp) {
|
||||
if (fseek(fp, 0l, SEEK_END) == -1)
|
||||
return NULL;
|
||||
|
||||
14
util.h
14
util.h
@@ -6,7 +6,15 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#if defined(_WIN32) || defined(WIN32)
|
||||
#include <windows.h> /* SSIZE_T */
|
||||
typedef SSIZE_T ssize_t;
|
||||
#else
|
||||
#include <unistd.h> /* ssize_t */
|
||||
#endif
|
||||
|
||||
typedef uint8_t pseudo_void;
|
||||
|
||||
/* some ANSI color codes */
|
||||
#define C_RED "\x1b[31m"
|
||||
@@ -27,6 +35,8 @@
|
||||
|
||||
#define C_RESET "\x1b[m"
|
||||
|
||||
void sleep_secs(double secs);
|
||||
|
||||
#define ERRSZ 4096
|
||||
extern char errbuf[ERRSZ];
|
||||
extern bool err;
|
||||
@@ -67,6 +77,8 @@ char *psndup(Pool *p, const char *s, size_t n);
|
||||
intmax_t stoimax(const char *s, size_t n, size_t base, ssize_t *endpos /* -1 on success */);
|
||||
/* convert a non-null-terminated string to a double */
|
||||
double stod(const char *s, size_t n, ssize_t *endpos /* -1 on success */);
|
||||
/* return the escape sequence for a given character; return NULL if there is none */
|
||||
const char *unescape_char(char c);
|
||||
|
||||
/* sets errno on failure */
|
||||
char *mreadfile(FILE *fp);
|
||||
|
||||
151
vm.c
151
vm.c
@@ -3,33 +3,50 @@
|
||||
#include "runtime.h"
|
||||
#include "util.h"
|
||||
|
||||
#define INIT_STACK_CAP 256
|
||||
#define INIT_STACK_CAP 128
|
||||
|
||||
typedef struct Stack {
|
||||
Value *mem;
|
||||
bool *holds_value;
|
||||
size_t len, cap;
|
||||
} Stack;
|
||||
|
||||
static Stack stack_make(void);
|
||||
static void stack_term(Stack *s);
|
||||
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) {
|
||||
Stack s;
|
||||
s.mem = xmalloc(sizeof(Value) * INIT_STACK_CAP);
|
||||
s.holds_value = xmalloc(sizeof(bool) * INIT_STACK_CAP);
|
||||
s.cap = INIT_STACK_CAP;
|
||||
s.len = 0;
|
||||
for (size_t i = 0; i < s.cap; i++)
|
||||
s.holds_value[i] = false;
|
||||
return 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->holds_value);
|
||||
}
|
||||
|
||||
static void stack_fit(Stack *s, size_t idx) {
|
||||
size_t size = idx+1;
|
||||
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,67 +59,161 @@ static Value *irparam_to_val(Stack *s, IRParam *v) {
|
||||
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 */
|
||||
size_t fn_args_cap = 16;
|
||||
Value *fn_args = xmalloc(sizeof(Value) * fn_args_cap);
|
||||
|
||||
/* so we can use index-based addressing */
|
||||
irlist_update_index(ir);
|
||||
|
||||
Stack s = stack_make();
|
||||
for (size_t i = 0; i < ir->len;) {
|
||||
IRTok *instr = &ir->toks[i];
|
||||
for (IRItem *i = ir->begin; i;) {
|
||||
IRTok *instr = &i->tok;
|
||||
err_ln = instr->ln;
|
||||
err_col = instr->col;
|
||||
switch (instr->instr) {
|
||||
case IRSet:
|
||||
case IRNeg:
|
||||
case IRNot:
|
||||
stack_fit(&s, instr->Unary.addr);
|
||||
TRY_ELSE(s.mem[instr->Unary.addr] = eval_unary(instr->instr, irparam_to_val(&s, &instr->Unary.val)),
|
||||
case IRNot: {
|
||||
Value res;
|
||||
TRY_ELSE(res = eval_unary(instr->instr, irparam_to_val(&s, &instr->Unary.val)),
|
||||
{free(fn_args); stack_term(&s);});
|
||||
stack_assign(&s, instr->Unary.addr, &res);
|
||||
break;
|
||||
}
|
||||
case IRAddrOf: {
|
||||
if (instr->Unary.val.kind != IRParamAddr) {
|
||||
set_err("Unable to take the address of a literal");
|
||||
free(fn_args);
|
||||
stack_term(&s);
|
||||
return;
|
||||
}
|
||||
Value *v = &s.mem[instr->Unary.val.Addr];
|
||||
Value res = {
|
||||
.type = TypePtr,
|
||||
.Ptr = {
|
||||
.type = v->type,
|
||||
.val = &v->Void,
|
||||
},
|
||||
};
|
||||
stack_assign(&s, instr->Unary.addr, &res);
|
||||
break;
|
||||
}
|
||||
case IRAdd:
|
||||
case IRSub:
|
||||
case IRDiv:
|
||||
case IRMul:
|
||||
case IREq:
|
||||
case IRNeq:
|
||||
case IRLt:
|
||||
case IRLe:
|
||||
case IRAnd:
|
||||
case IROr:
|
||||
stack_fit(&s, instr->Binary.addr);
|
||||
TRY_ELSE(s.mem[instr->Binary.addr] = eval_binary(instr->instr,
|
||||
case IROr: {
|
||||
Value res;
|
||||
TRY_ELSE(res = eval_binary(instr->instr,
|
||||
irparam_to_val(&s, &instr->Binary.lhs),
|
||||
irparam_to_val(&s, &instr->Binary.rhs)),
|
||||
{free(fn_args); stack_term(&s);});
|
||||
stack_assign(&s, instr->Binary.addr, &res);
|
||||
break;
|
||||
}
|
||||
case IRJmp:
|
||||
i = instr->Jmp.iaddr;
|
||||
if (instr->Jmp.iaddr < ir->len)
|
||||
i = ir->index[instr->Jmp.iaddr];
|
||||
else
|
||||
i = NULL;
|
||||
continue;
|
||||
case IRJnz:
|
||||
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;
|
||||
}
|
||||
break;
|
||||
case IRCallInternal: {
|
||||
const BuiltinFunc *f = &builtin_funcs[instr->CallI.fid];
|
||||
size_t n_args = instr->CallI.n_args;
|
||||
/* make sure enough space for our arguments is allocated */
|
||||
if (f->n_args > fn_args_cap)
|
||||
fn_args = xrealloc(fn_args, sizeof(Value) * (fn_args_cap = f->n_args));
|
||||
if (n_args > fn_args_cap)
|
||||
fn_args = xrealloc(fn_args, sizeof(Value) * (fn_args_cap = n_args));
|
||||
/* copy arguments into buffer */
|
||||
for (size_t i = 0; i < f->n_args; i++)
|
||||
for (size_t i = 0; i < n_args; i++)
|
||||
fn_args[i] = *irparam_to_val(&s, &instr->CallI.args[i]);
|
||||
|
||||
stack_fit(&s, instr->CallI.ret_addr);
|
||||
TRY_ELSE(s.mem[instr->CallI.ret_addr] = f->func(fn_args),
|
||||
{free(fn_args); stack_term(&s);});
|
||||
if (f->returns) {
|
||||
Value res;
|
||||
if (f->kind == FuncVarArgs) {
|
||||
size_t min_args = f->VarArgs.min_args;
|
||||
TRY_ELSE(res = f->VarArgs.WithRet.func(n_args - min_args, fn_args),
|
||||
{free(fn_args); stack_term(&s);});
|
||||
} else if (f->kind == FuncFixedArgs) {
|
||||
TRY_ELSE(res = f->FixedArgs.WithRet.func(fn_args),
|
||||
{free(fn_args); stack_term(&s);});
|
||||
} else
|
||||
ASSERT_UNREACHED();
|
||||
stack_assign(&s, instr->CallI.ret_addr, &res);
|
||||
} else {
|
||||
if (f->kind == FuncVarArgs) {
|
||||
size_t min_args = f->VarArgs.min_args;
|
||||
TRY_ELSE(f->VarArgs.NoRet.func(n_args - min_args, fn_args),
|
||||
{free(fn_args); stack_term(&s);});
|
||||
} else if (f->kind == FuncFixedArgs) {
|
||||
TRY_ELSE(f->FixedArgs.NoRet.func(fn_args),
|
||||
{free(fn_args); stack_term(&s);});
|
||||
} else
|
||||
ASSERT_UNREACHED();
|
||||
}
|
||||
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:
|
||||
ASSERT_UNREACHED();
|
||||
}
|
||||
|
||||
i++;
|
||||
i = i->next;
|
||||
}
|
||||
stack_term(&s);
|
||||
free(fn_args);
|
||||
|
||||
Reference in New Issue
Block a user