Compare commits
51 Commits
63af3e907b
...
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 | ||
|
|
92c4c5c991 | ||
|
|
9bd4d4e0c4 | ||
| bb75b78a36 | |||
|
|
ca232fbf6a | ||
|
|
6bdc4e3210 | ||
|
|
d67008cfbf | ||
|
|
a0842424ec | ||
|
|
84785dc3cf | ||
|
|
d185396a1c | ||
|
|
97e8e32ebc | ||
|
|
ef63742015 | ||
|
|
7ae9ddaee9 | ||
|
|
298883939b | ||
|
|
052e78bf2e | ||
|
|
d7860fdac0 | ||
|
|
5dd15ce9f1 | ||
|
|
41a5dba208 | ||
|
|
b80e5a9c4e | ||
|
|
e7f4773cba | ||
|
|
a8be4540b1 | ||
|
|
9f339ed44d | ||
|
|
10d436107c | ||
|
|
61d5661b96 | ||
|
|
005309d1eb | ||
|
|
6080901842 |
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 = -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 = -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
|
#CFLAGS = -O3 -std=c11 -Wall -Wextra -pedantic -Wmissing-prototypes -Wstrict-prototypes -Wold-style-definition
|
||||||
@@ -12,7 +15,7 @@ $(EXE): $(OBJ)
|
|||||||
$(CC) -o $@ $^ $(CFLAGS) $(LDFLAGS)
|
$(CC) -o $@ $^ $(CFLAGS) $(LDFLAGS)
|
||||||
|
|
||||||
%.o: %.c
|
%.o: %.c
|
||||||
$(CC) -c -o $@ $< $(CFLAGS)
|
$(CC) -c -o $@ $< $(CPPFLAGS) $(CFLAGS)
|
||||||
|
|
||||||
deps.mk: $(SOURCE) $(HEADERS)
|
deps.mk: $(SOURCE) $(HEADERS)
|
||||||
@echo "# Automatically generated by $(CC) -MM." > $@
|
@echo "# Automatically generated by $(CC) -MM." > $@
|
||||||
|
|||||||
@@ -1,3 +1 @@
|
|||||||
# lang
|
# lang
|
||||||
|
|
||||||
Yet another useless programming language (a lot of stuff not yet implemented).
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
a := 1
|
|
||||||
a = a + 1
|
|
||||||
b := a - 2 * 3
|
|
||||||
b = 2 + b * a
|
|
||||||
c := -b
|
|
||||||
|
|
||||||
//a := 1
|
|
||||||
//b := 1 - 2 * 2 + 5
|
|
||||||
//c := a + b * 2 * b
|
|
||||||
//d := a + 4 * b * a
|
|
||||||
|
|
||||||
/*x := 1
|
|
||||||
y := 1
|
|
||||||
|
|
||||||
i := 60
|
|
||||||
while i {
|
|
||||||
z := x + y
|
|
||||||
y = x
|
|
||||||
x = z
|
|
||||||
print(z)
|
|
||||||
i = i - 1
|
|
||||||
}*/
|
|
||||||
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)
|
||||||
|
}
|
||||||
12
examples/fib.script
Normal file
12
examples/fib.script
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
x := 1
|
||||||
|
y := 1
|
||||||
|
|
||||||
|
i := 60
|
||||||
|
|
||||||
|
while i {
|
||||||
|
z := x + y
|
||||||
|
y = x
|
||||||
|
x = z
|
||||||
|
putln(z)
|
||||||
|
i = i - 1
|
||||||
|
}
|
||||||
86
examples/mandelbrot.script
Normal file
86
examples/mandelbrot.script
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
width := 280
|
||||||
|
height := 100
|
||||||
|
|
||||||
|
iterations := 100
|
||||||
|
xmin := -2.0
|
||||||
|
xmax := 0.5
|
||||||
|
ymin := -1.0
|
||||||
|
ymax := 1.0
|
||||||
|
|
||||||
|
// Some further coordinates:
|
||||||
|
/*iterations := 1000
|
||||||
|
xmin := -0.9072945999
|
||||||
|
xmax := -0.8984310833
|
||||||
|
ymin := 0.2304178999
|
||||||
|
ymax := 0.2370858666*/
|
||||||
|
|
||||||
|
/*iterations := 100
|
||||||
|
xmin := -0.193596288
|
||||||
|
xmax := -0.119260320
|
||||||
|
ymin := 1.006960992
|
||||||
|
ymax := 1.062687264*/
|
||||||
|
|
||||||
|
/*iterations := 800
|
||||||
|
xmin := -0.1675326254
|
||||||
|
xmax := -0.1675148625
|
||||||
|
ymin := 1.0413005672
|
||||||
|
ymax := 1.0413138086*/
|
||||||
|
|
||||||
|
/*iterations := 918
|
||||||
|
xmin := -0.7506201104
|
||||||
|
xmax := -0.7503409687
|
||||||
|
ymin := 0.0170447020
|
||||||
|
ymax := 0.0172540583*/
|
||||||
|
|
||||||
|
/*iterations := 400
|
||||||
|
xmin := -0.7548484315
|
||||||
|
xmax := -0.7540548595
|
||||||
|
ymin := 0.0530077004
|
||||||
|
ymax := 0.0536039518*/
|
||||||
|
|
||||||
|
y := 0
|
||||||
|
while y < height {
|
||||||
|
c_im := (float(height - y) / float(height)) * (ymax - ymin) + ymin
|
||||||
|
x := 0
|
||||||
|
while x < width {
|
||||||
|
c_re := (float(x) / float(width)) * (xmax - xmin) + xmin
|
||||||
|
|
||||||
|
z_re := 0.0
|
||||||
|
z_im := 0.0
|
||||||
|
|
||||||
|
it := 0
|
||||||
|
loop := true
|
||||||
|
while it < iterations && loop {
|
||||||
|
/* z = z*z + c */
|
||||||
|
/* (a + bi)^2 = a^2 + 2abi - b^2 */
|
||||||
|
z_re_tmp := z_re * z_re - z_im * z_im + c_re
|
||||||
|
z_im = 2.0 * z_re * z_im + c_im
|
||||||
|
z_re = z_re_tmp
|
||||||
|
|
||||||
|
/* Break if the number shoots off to infinity. */
|
||||||
|
if z_re * z_re + z_im * z_im > 4.0 {
|
||||||
|
loop = false
|
||||||
|
}
|
||||||
|
|
||||||
|
it = it + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
if it <= iterations / 5
|
||||||
|
put(' ')
|
||||||
|
else if it <= iterations / 5 * 2
|
||||||
|
put('.')
|
||||||
|
else if it <= iterations / 5 * 3
|
||||||
|
put(',')
|
||||||
|
else if it <= iterations / 5 * 4
|
||||||
|
put('*')
|
||||||
|
else if it < iterations
|
||||||
|
put('+')
|
||||||
|
else if it == iterations
|
||||||
|
put('#')
|
||||||
|
|
||||||
|
x = x + 1
|
||||||
|
}
|
||||||
|
put('\n')
|
||||||
|
|
||||||
|
y = y + 1
|
||||||
|
}
|
||||||
17
examples/pi.script
Normal file
17
examples/pi.script
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
sum := 0.0
|
||||||
|
k := 0
|
||||||
|
|
||||||
|
iterations := 100
|
||||||
|
|
||||||
|
while k < iterations {
|
||||||
|
k_f := float(k)
|
||||||
|
sum = sum + 1.0 / pow(16.0, k_f) *
|
||||||
|
(4.0 / (8.0 * k_f + 1.0) -
|
||||||
|
2.0 / (8.0 * k_f + 4.0) -
|
||||||
|
1.0 / (8.0 * k_f + 5.0) -
|
||||||
|
1.0 / (8.0 * k_f + 6.0))
|
||||||
|
k = k + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
put("π ≈ ")
|
||||||
|
putln(sum)
|
||||||
264
ir.c
264
ir.c
@@ -4,101 +4,231 @@
|
|||||||
#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",
|
||||||
[IRPrint] = "print",
|
[IREq] = "eq",
|
||||||
[IRJnz] = "jnz",
|
[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 4096
|
#define IRLIST_INIT_CAP_LONG 4096
|
||||||
|
#define IRLIST_INIT_CAP_SHORT 16
|
||||||
|
|
||||||
void irtoks_init(IRToks *v) {
|
static void irlist_init_with_cap(IRList *v, size_t cap);
|
||||||
v->toks = malloc(sizeof(IRTok) * IRTOKS_INIT_CAP);
|
static IRItem *irlist_new_item(IRList *v);
|
||||||
|
|
||||||
|
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->len = 0;
|
||||||
v->cap = IRTOKS_INIT_CAP;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void irtoks_term(IRToks *v) {
|
static IRItem *irlist_new_item(IRList *v) {
|
||||||
for (size_t i = 0; i < v->len; i++) {
|
IRItem *ret = pool_alloc(v->p, sizeof(IRItem));
|
||||||
if (v->toks[i].instr == IRPrint) {
|
ret->next = NULL;
|
||||||
for (IRArgs *a = v->toks[i].Print.args; a != NULL;) {
|
return ret;
|
||||||
IRArgs *next = a->next;
|
|
||||||
free(a);
|
|
||||||
a = next;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
free(v->toks);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void irtoks_app(IRToks *v, IRTok t) {
|
void irlist_init_long(IRList *v) {
|
||||||
if (v->len+1 > v->cap)
|
irlist_init_with_cap(v, IRLIST_INIT_CAP_LONG);
|
||||||
v->toks = realloc(v->toks, sizeof(IRTok) * (v->cap *= 2));
|
|
||||||
v->toks[v->len++] = t;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void print_val(const Value *v);
|
void irlist_init_short(IRList *v) {
|
||||||
static void print_irparam(const IRParam *p);
|
irlist_init_with_cap(v, IRLIST_INIT_CAP_SHORT);
|
||||||
|
|
||||||
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;
|
|
||||||
default:
|
|
||||||
printf("(unknown type)");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void print_irparam(const IRParam *p) {
|
static void free_irparam(IRParam *v, bool purge);
|
||||||
if (p->kind == IRParamLiteral) {
|
|
||||||
print_val(&p->Literal);
|
/* if purge is set, even statically allocated literals are freed */
|
||||||
} else if (p->kind == IRParamAddr) {
|
static void free_irparam(IRParam *v, bool purge) {
|
||||||
printf("%%%zd", p->Addr);
|
if (v->kind == IRParamLiteral)
|
||||||
}
|
free_value(&v->Literal, purge);
|
||||||
}
|
}
|
||||||
|
|
||||||
void print_ir(IRToks *v) {
|
void irlist_term(IRList *v) {
|
||||||
for (size_t i = 0; i < v->len; i++) {
|
for (IRItem *i = v->begin; i; i = i->next) {
|
||||||
printf("%s", irinstr_str[v->toks[i].instr]);
|
switch (i->tok.instr) {
|
||||||
switch (v->toks[i].instr) {
|
|
||||||
case IRSet:
|
case IRSet:
|
||||||
case IRNeg:
|
case IRNeg:
|
||||||
printf(" %%%zu ", v->toks[i].Unary.addr);
|
case IRNot:
|
||||||
print_irparam(&v->toks[i].Unary.val);
|
case IRAddrOf:
|
||||||
|
free_irparam(&i->tok.Unary.val, true);
|
||||||
break;
|
break;
|
||||||
case IRAdd:
|
case IRAdd:
|
||||||
case IRSub:
|
case IRSub:
|
||||||
case IRDiv:
|
case IRDiv:
|
||||||
case IRMul:
|
case IRMul:
|
||||||
printf(" %%%zu ", v->toks[i].Arith.addr);
|
case IREq:
|
||||||
print_irparam(&v->toks[i].Arith.lhs);
|
case IRNeq:
|
||||||
printf(" ");
|
case IRLt:
|
||||||
print_irparam(&v->toks[i].Arith.rhs);
|
case IRLe:
|
||||||
|
case IRAnd:
|
||||||
|
case IROr:
|
||||||
|
free_irparam(&i->tok.Binary.lhs, true);
|
||||||
|
free_irparam(&i->tok.Binary.rhs, true);
|
||||||
break;
|
break;
|
||||||
case IRPrint:
|
case IRJmp:
|
||||||
for (IRArgs *a = v->toks[i].Print.args; a != NULL; a = a->next) {
|
break;
|
||||||
printf(" ");
|
case IRJnz:
|
||||||
print_irparam(&a->param);
|
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;
|
break;
|
||||||
case IRJnz:
|
case IRJnz:
|
||||||
printf(" ");
|
printf(" ");
|
||||||
print_irparam(&v->toks[i].CJmp.condition);
|
print_irparam(&i->tok.CJmp.condition);
|
||||||
printf(" %zu", v->toks[i].CJmp.iaddr);
|
printf(" %zx", i->tok.CJmp.iaddr);
|
||||||
break;
|
break;
|
||||||
default:
|
case IRCallInternal: {
|
||||||
|
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(&i->tok.CallI.args[j]);
|
||||||
|
}
|
||||||
break;
|
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");
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
90
ir.h
90
ir.h
@@ -3,6 +3,36 @@
|
|||||||
|
|
||||||
#include "tok.h"
|
#include "tok.h"
|
||||||
|
|
||||||
|
typedef struct BuiltinFunc {
|
||||||
|
enum {
|
||||||
|
FuncFixedArgs,
|
||||||
|
FuncVarArgs,
|
||||||
|
} kind;
|
||||||
|
|
||||||
|
bool returns : 1;
|
||||||
|
bool side_effects : 1;
|
||||||
|
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 {
|
enum IRInstr {
|
||||||
IRSet,
|
IRSet,
|
||||||
IRNeg,
|
IRNeg,
|
||||||
@@ -10,8 +40,18 @@ enum IRInstr {
|
|||||||
IRSub,
|
IRSub,
|
||||||
IRMul,
|
IRMul,
|
||||||
IRDiv,
|
IRDiv,
|
||||||
IRPrint,
|
IREq,
|
||||||
|
IRNeq,
|
||||||
|
IRLt,
|
||||||
|
IRLe,
|
||||||
|
IRNot,
|
||||||
|
IRAnd,
|
||||||
|
IROr,
|
||||||
|
IRJmp,
|
||||||
IRJnz,
|
IRJnz,
|
||||||
|
IRCallInternal,
|
||||||
|
IRAddrOf,
|
||||||
|
IRArrMake,
|
||||||
IRInstrEnumSize,
|
IRInstrEnumSize,
|
||||||
};
|
};
|
||||||
typedef enum IRInstr IRInstr;
|
typedef enum IRInstr IRInstr;
|
||||||
@@ -50,29 +90,53 @@ typedef struct IRTok {
|
|||||||
struct {
|
struct {
|
||||||
size_t addr;
|
size_t addr;
|
||||||
IRParam lhs, rhs;
|
IRParam lhs, rhs;
|
||||||
} Arith;
|
} Binary;
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
IRArgs *args;
|
size_t iaddr;
|
||||||
size_t args_size;
|
} Jmp;
|
||||||
} Print;
|
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
size_t iaddr;
|
size_t iaddr;
|
||||||
IRParam condition;
|
IRParam condition;
|
||||||
} CJmp;
|
} CJmp;
|
||||||
|
|
||||||
|
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;
|
} IRTok;
|
||||||
|
|
||||||
typedef struct IRToks {
|
typedef struct IRItem {
|
||||||
size_t len, cap;
|
struct IRItem *next;
|
||||||
IRTok *toks;
|
IRTok tok;
|
||||||
} IRToks;
|
} IRItem;
|
||||||
|
|
||||||
void irtoks_init(IRToks *v);
|
typedef struct IRList {
|
||||||
void irtoks_term(IRToks *v);
|
IRItem *begin, *end;
|
||||||
void irtoks_app(IRToks *v, IRTok t);
|
Pool *p;
|
||||||
|
IRItem **index; /* index to pointer, irlist_update_index() must be called before use */
|
||||||
|
size_t len;
|
||||||
|
} IRList;
|
||||||
|
|
||||||
void print_ir(IRToks *v);
|
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 */
|
#endif /* IR_H */
|
||||||
|
|||||||
162
lex.c
162
lex.c
@@ -2,6 +2,8 @@
|
|||||||
|
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
|
||||||
|
#define TAB_WIDTH 4
|
||||||
|
|
||||||
typedef struct Pos {
|
typedef struct Pos {
|
||||||
size_t ln, col; /* current position */
|
size_t ln, col; /* current position */
|
||||||
size_t m_ln, m_col; /* marked position */
|
size_t m_ln, m_col; /* marked position */
|
||||||
@@ -11,12 +13,15 @@ static void consume(Pos *p, char c);
|
|||||||
static void emit(TokList *toks, const Pos *p, Tok t);
|
static void emit(TokList *toks, const Pos *p, Tok t);
|
||||||
static void mark(Pos *p);
|
static void mark(Pos *p);
|
||||||
static void mark_err(const Pos *p);
|
static void mark_err(const Pos *p);
|
||||||
|
static char get_esc_char(char c);
|
||||||
|
|
||||||
static void consume(Pos *p, char c) {
|
static void consume(Pos *p, char c) {
|
||||||
if (c == '\n') {
|
if (c == '\n') {
|
||||||
p->ln++;
|
p->ln++;
|
||||||
p->col = 1;
|
p->col = 1;
|
||||||
} else
|
} else if (c == '\t')
|
||||||
|
p->col += TAB_WIDTH;
|
||||||
|
else
|
||||||
p->col++;
|
p->col++;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -36,6 +41,23 @@ static void mark_err(const Pos *p) {
|
|||||||
err_col = p->m_col;
|
err_col = p->m_col;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static char get_esc_char(char c) {
|
||||||
|
switch(c) {
|
||||||
|
case 'a': return '\a';
|
||||||
|
case 'b': return '\b';
|
||||||
|
case 'e': return '\033';
|
||||||
|
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 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
TokList lex(const char *s) {
|
TokList lex(const char *s) {
|
||||||
TokList toks;
|
TokList toks;
|
||||||
toklist_init(&toks);
|
toklist_init(&toks);
|
||||||
@@ -54,8 +76,14 @@ TokList lex(const char *s) {
|
|||||||
}
|
}
|
||||||
if (streq_0_n("if", start, i))
|
if (streq_0_n("if", start, i))
|
||||||
emit(&toks, &pos, (Tok){ .kind = TokIf });
|
emit(&toks, &pos, (Tok){ .kind = TokIf });
|
||||||
|
else if (streq_0_n("else", start, i))
|
||||||
|
emit(&toks, &pos, (Tok){ .kind = TokElse });
|
||||||
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))
|
||||||
|
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 = TypeBool, .Bool = false }});
|
||||||
else {
|
else {
|
||||||
emit(&toks, &pos, (Tok){
|
emit(&toks, &pos, (Tok){
|
||||||
.kind = TokIdent,
|
.kind = TokIdent,
|
||||||
@@ -118,9 +146,7 @@ TokList lex(const char *s) {
|
|||||||
emit(&toks, &pos, (Tok){
|
emit(&toks, &pos, (Tok){
|
||||||
.kind = TokVal,
|
.kind = TokVal,
|
||||||
.Val = {
|
.Val = {
|
||||||
.type = {
|
.type = TypeFloat,
|
||||||
.kind = TypeFloat,
|
|
||||||
},
|
|
||||||
.Float = num,
|
.Float = num,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@@ -136,9 +162,7 @@ TokList lex(const char *s) {
|
|||||||
emit(&toks, &pos, (Tok){
|
emit(&toks, &pos, (Tok){
|
||||||
.kind = TokVal,
|
.kind = TokVal,
|
||||||
.Val = {
|
.Val = {
|
||||||
.type = {
|
.type = TypeInt,
|
||||||
.kind = TypeInt,
|
|
||||||
},
|
|
||||||
.Int = num,
|
.Int = num,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@@ -160,20 +184,71 @@ TokList lex(const char *s) {
|
|||||||
break;
|
break;
|
||||||
case ':':
|
case ':':
|
||||||
consume(&pos, *(s++));
|
consume(&pos, *(s++));
|
||||||
if (s[0] == '=') {
|
if (s[0] == '=')
|
||||||
emit(&toks, &pos, (Tok){ .kind = TokDeclare });
|
emit(&toks, &pos, (Tok){ .kind = TokDeclare });
|
||||||
} else {
|
else {
|
||||||
set_err("Expected ':='");
|
set_err("Expected ':='");
|
||||||
return toks;
|
return toks;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case '=':
|
case '=':
|
||||||
emit(&toks, &pos, (Tok){ .kind = TokAssign });
|
consume(&pos, *(s++));
|
||||||
|
if (s[0] == '=')
|
||||||
|
emit(&toks, &pos, (Tok){ .kind = TokOp, .Op = OpEq });
|
||||||
|
else {
|
||||||
|
emit(&toks, &pos, (Tok){ .kind = TokAssign });
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case '<':
|
||||||
|
consume(&pos, *(s++));
|
||||||
|
if (s[0] == '=')
|
||||||
|
emit(&toks, &pos, (Tok){ .kind = TokOp, .Op = OpLe });
|
||||||
|
else {
|
||||||
|
emit(&toks, &pos, (Tok){ .kind = TokOp, .Op = OpLt });
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case '>':
|
||||||
|
consume(&pos, *(s++));
|
||||||
|
if (s[0] == '=')
|
||||||
|
emit(&toks, &pos, (Tok){ .kind = TokOp, .Op = OpGe });
|
||||||
|
else {
|
||||||
|
emit(&toks, &pos, (Tok){ .kind = TokOp, .Op = OpGt });
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case '&':
|
||||||
|
consume(&pos, *(s++));
|
||||||
|
if (s[0] == '&')
|
||||||
|
emit(&toks, &pos, (Tok){ .kind = TokOp, .Op = OpAnd });
|
||||||
|
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] == '|')
|
||||||
|
emit(&toks, &pos, (Tok){ .kind = TokOp, .Op = OpOr });
|
||||||
|
else
|
||||||
|
continue;
|
||||||
break;
|
break;
|
||||||
case '{':
|
case '{':
|
||||||
case '}':
|
case '}':
|
||||||
case '(':
|
case '(':
|
||||||
case ')':
|
case ')':
|
||||||
|
case '[':
|
||||||
|
case ']':
|
||||||
case ',':
|
case ',':
|
||||||
case '+':
|
case '+':
|
||||||
case '-':
|
case '-':
|
||||||
@@ -217,6 +292,73 @@ TokList lex(const char *s) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
|
case '\'': {
|
||||||
|
consume(&pos, *(s++));
|
||||||
|
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]);
|
||||||
|
return toks;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
consume(&pos, *(s++));
|
||||||
|
if (s[0] != '\'') {
|
||||||
|
set_err("Unclosed char literal");
|
||||||
|
return toks;
|
||||||
|
}
|
||||||
|
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:
|
default:
|
||||||
set_err("Unrecognized character: '%c'", s[0]);
|
set_err("Unrecognized character: '%c'", s[0]);
|
||||||
return toks;
|
return toks;
|
||||||
|
|||||||
208
main.c
208
main.c
@@ -8,7 +8,9 @@
|
|||||||
#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"
|
||||||
|
|
||||||
static void usage(const char *prgname);
|
static void usage(const char *prgname);
|
||||||
static void die(const char *fmt, ...);
|
static void die(const char *fmt, ...);
|
||||||
@@ -32,6 +34,181 @@ static void die(const char *fmt, ...) {
|
|||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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(" ");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void fn_putln(size_t extra_args, Value *args) {
|
||||||
|
fn_put(extra_args, args);
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
static Value fn_int(Value *args) {
|
||||||
|
Value ret = {
|
||||||
|
.type = TypeInt,
|
||||||
|
.Int = 0,
|
||||||
|
};
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Value fn_float(Value *args) {
|
||||||
|
Value ret = {
|
||||||
|
.type = TypeFloat,
|
||||||
|
.Float = 0.0,
|
||||||
|
};
|
||||||
|
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 == TypeFloat && args[1].type == TypeFloat)) {
|
||||||
|
set_err("pow() requires arguments of type float");
|
||||||
|
return (Value){0};
|
||||||
|
}
|
||||||
|
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) {
|
int main(int argc, const char **argv) {
|
||||||
/* parse arguments */
|
/* parse arguments */
|
||||||
size_t nargs = argc - 1;
|
size_t nargs = argc - 1;
|
||||||
@@ -88,17 +265,38 @@ int main(int argc, const char **argv) {
|
|||||||
if (opt_emit_tokens)
|
if (opt_emit_tokens)
|
||||||
print_toks(&tokens);
|
print_toks(&tokens);
|
||||||
/* parse tokens into IR code */
|
/* parse tokens into IR code */
|
||||||
IRToks ir = parse(&tokens);
|
BuiltinFunc funcs[] = {
|
||||||
|
{ .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, }},
|
||||||
|
};
|
||||||
|
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);
|
||||||
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;
|
||||||
}
|
}
|
||||||
toklist_term(&tokens);
|
toklist_term(&tokens);
|
||||||
|
optimize_ir(&ir);
|
||||||
if (opt_emit_ir)
|
if (opt_emit_ir)
|
||||||
print_ir(&ir);
|
print_ir(&ir, funcs);
|
||||||
/* run the IR */
|
/* run the IR */
|
||||||
/* TODO... */
|
if (!opt_dry) {
|
||||||
irtoks_term(&ir);
|
run(&ir, funcs);
|
||||||
|
if (err) {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
irlist_term(&ir);
|
||||||
}
|
}
|
||||||
|
|||||||
2
map.c
2
map.c
@@ -14,7 +14,7 @@ static void init_with_cap(Map *m, size_t val_size, size_t cap) {
|
|||||||
m->len = 0;
|
m->len = 0;
|
||||||
m->cap = cap;
|
m->cap = cap;
|
||||||
m->val_size = val_size;
|
m->val_size = val_size;
|
||||||
void *data = malloc(sizeof(MapSlot) * cap + val_size * cap);
|
void *data = xmalloc(sizeof(MapSlot) * cap + val_size * cap);
|
||||||
m->slots = data;
|
m->slots = data;
|
||||||
m->vals = m->slots + cap;
|
m->vals = m->slots + cap;
|
||||||
for (size_t i = 0; i < cap; i++) {
|
for (size_t i = 0; i < cap; i++) {
|
||||||
|
|||||||
2
parse.h
2
parse.h
@@ -5,6 +5,6 @@
|
|||||||
#include "tok.h"
|
#include "tok.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
|
||||||
IRToks parse(TokList *toks);
|
IRList parse(TokList *toks, BuiltinFunc *builtin_funcs, size_t n_builtin_funcs);
|
||||||
|
|
||||||
#endif /* PARSE_H */
|
#endif /* PARSE_H */
|
||||||
|
|||||||
120
runtime.c
120
runtime.c
@@ -2,55 +2,151 @@
|
|||||||
|
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
|
||||||
Value eval_arith(IRInstr instr, const Value *lhs, const Value *rhs) {
|
Value eval_binary(IRInstr instr, const Value *lhs, const Value *rhs) {
|
||||||
switch (instr) {
|
switch (instr) {
|
||||||
case IRAdd:
|
case IRAdd:
|
||||||
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;
|
||||||
case IRSub: res = lhs->Int - rhs->Int; break;
|
case IRSub: res = lhs->Int - rhs->Int; break;
|
||||||
case IRMul: res = lhs->Int * rhs->Int; break;
|
case IRMul: res = lhs->Int * rhs->Int; break;
|
||||||
case IRDiv: res = lhs->Int / rhs->Int; break;
|
case IRDiv: res = lhs->Int / rhs->Int; break;
|
||||||
default: break;
|
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) {
|
||||||
float res;
|
double res;
|
||||||
switch (instr) {
|
switch (instr) {
|
||||||
case IRAdd: res = lhs->Float + rhs->Float; break;
|
case IRAdd: res = lhs->Float + rhs->Float; break;
|
||||||
case IRSub: res = lhs->Float - rhs->Float; break;
|
case IRSub: res = lhs->Float - rhs->Float; break;
|
||||||
case IRMul: res = lhs->Float * rhs->Float; break;
|
case IRMul: res = lhs->Float * rhs->Float; break;
|
||||||
case IRDiv: res = lhs->Float / rhs->Float; break;
|
case IRDiv: res = lhs->Float / rhs->Float; break;
|
||||||
default: break;
|
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'", 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){0};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
case IREq:
|
||||||
|
case IRNeq:
|
||||||
|
case IRLt:
|
||||||
|
case IRLe: {
|
||||||
|
bool res;
|
||||||
|
if (lhs->type == TypeInt && rhs->type == TypeInt) {
|
||||||
|
switch (instr) {
|
||||||
|
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 == TypeFloat && rhs->type == TypeFloat) {
|
||||||
|
switch (instr) {
|
||||||
|
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': %s and %s", irinstr_str[instr], type_str[lhs->type], type_str[rhs->type]);
|
||||||
|
return (Value){0};
|
||||||
|
}
|
||||||
|
return (Value){
|
||||||
|
.type = TypeBool,
|
||||||
|
.Bool = res,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
case IRAnd:
|
||||||
|
return (Value){
|
||||||
|
.type = TypeBool,
|
||||||
|
.Bool = is_nonzero(lhs) && is_nonzero(rhs),
|
||||||
|
};
|
||||||
|
break;
|
||||||
|
case IROr:
|
||||||
|
return (Value){
|
||||||
|
.type = TypeBool,
|
||||||
|
.Bool = is_nonzero(lhs) || is_nonzero(rhs),
|
||||||
|
};
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
ASSERT_UNREACHED();
|
ASSERT_UNREACHED();
|
||||||
}
|
}
|
||||||
return (Value){0};
|
return (Value){0};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Value eval_unary(IRInstr instr, const Value *v) {
|
||||||
|
switch (instr) {
|
||||||
|
case IRSet:
|
||||||
|
return *v;
|
||||||
|
case IRNeg:
|
||||||
|
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': %s", irinstr_str[instr], type_str[v->type]);
|
||||||
|
return (Value){0};
|
||||||
|
}
|
||||||
|
case IRNot:
|
||||||
|
if (v->type == TypeBool) {
|
||||||
|
return (Value){ .type = TypeBool, .Bool = !v->Bool };
|
||||||
|
} else {
|
||||||
|
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) {
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
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;
|
||||||
default: ASSERT_UNREACHED();
|
default: ASSERT_UNREACHED();
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
|
|||||||
@@ -3,7 +3,9 @@
|
|||||||
|
|
||||||
#include "ir.h"
|
#include "ir.h"
|
||||||
|
|
||||||
Value eval_arith(IRInstr instr, const Value *lhs, const Value *rhs);
|
Value eval_binary(IRInstr instr, const Value *lhs, const Value *rhs);
|
||||||
|
Value eval_unary(IRInstr instr, const Value *v);
|
||||||
|
bool is_nonzero(const Value *v);
|
||||||
Value zero_val(Type ty);
|
Value zero_val(Type ty);
|
||||||
|
|
||||||
#endif /* RUNTIME_H */
|
#endif /* RUNTIME_H */
|
||||||
|
|||||||
151
tok.c
151
tok.c
@@ -5,16 +5,132 @@
|
|||||||
|
|
||||||
#include "util.h"
|
#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] = {
|
int8_t op_prec[OperatorEnumSize] = {
|
||||||
[OpEOF] = PREC_DELIM,
|
[OpEOF] = PREC_DELIM,
|
||||||
[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,
|
||||||
[OpAdd] = 0,
|
[OpAnd] = 0,
|
||||||
[OpSub] = 0,
|
[OpOr] = 0,
|
||||||
[OpMul] = 1,
|
[OpEq] = 1,
|
||||||
[OpDiv] = 1,
|
[OpNeq] = 1,
|
||||||
|
[OpLt] = 1,
|
||||||
|
[OpGt] = 1,
|
||||||
|
[OpLe] = 1,
|
||||||
|
[OpGe] = 1,
|
||||||
|
[OpAdd] = 2,
|
||||||
|
[OpSub] = 2,
|
||||||
|
[OpMul] = 3,
|
||||||
|
[OpDiv] = 3,
|
||||||
};
|
};
|
||||||
|
|
||||||
const char *op_str[OperatorEnumSize] = {
|
const char *op_str[OperatorEnumSize] = {
|
||||||
@@ -22,19 +138,31 @@ const char *op_str[OperatorEnumSize] = {
|
|||||||
[OpRCurl] = "}",
|
[OpRCurl] = "}",
|
||||||
[OpLParen] = "(",
|
[OpLParen] = "(",
|
||||||
[OpRParen] = ")",
|
[OpRParen] = ")",
|
||||||
|
[OpLBrack] = "[",
|
||||||
|
[OpRBrack] = "]",
|
||||||
[OpComma] = ",",
|
[OpComma] = ",",
|
||||||
[OpAdd] = "+",
|
[OpAdd] = "+",
|
||||||
[OpSub] = "-",
|
[OpSub] = "-",
|
||||||
[OpMul] = "*",
|
[OpMul] = "*",
|
||||||
[OpDiv] = "/",
|
[OpDiv] = "/",
|
||||||
|
[OpNot] = "!",
|
||||||
[OpNewLn] = "\\n",
|
[OpNewLn] = "\\n",
|
||||||
[OpEOF] = "EOF",
|
[OpEOF] = "EOF",
|
||||||
|
[OpEq] = "==",
|
||||||
|
[OpNeq] = "!=",
|
||||||
|
[OpLt] = "<",
|
||||||
|
[OpGt] = ">",
|
||||||
|
[OpLe] = "<=",
|
||||||
|
[OpGe] = ">=",
|
||||||
|
[OpAnd] = "&&",
|
||||||
|
[OpOr] = "||",
|
||||||
};
|
};
|
||||||
|
|
||||||
const char *tok_str[TokKindEnumSize] = {
|
const char *tok_str[TokKindEnumSize] = {
|
||||||
[TokAssign] = "=",
|
[TokAssign] = "=",
|
||||||
[TokDeclare] = ":=",
|
[TokDeclare] = ":=",
|
||||||
[TokIf] = "if",
|
[TokIf] = "if",
|
||||||
|
[TokElse] = "else",
|
||||||
[TokWhile] = "while",
|
[TokWhile] = "while",
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -92,18 +220,9 @@ void print_toks(TokList *l) {
|
|||||||
printf(": " C_ICYAN "%s" C_RESET, op_str[i->tok.Op]);
|
printf(": " C_ICYAN "%s" C_RESET, op_str[i->tok.Op]);
|
||||||
break;
|
break;
|
||||||
case TokVal:
|
case TokVal:
|
||||||
printf(C_IYELLOW "Val" C_RESET);
|
printf(C_IYELLOW "Val" C_RESET ": " C_ICYAN);
|
||||||
switch (i->tok.Val.type.kind) {
|
print_value(&i->tok.Val, false);
|
||||||
case TypeFloat:
|
printf(C_RESET);
|
||||||
printf(": " C_ICYAN "%f" C_RESET, i->tok.Val.Float);
|
|
||||||
break;
|
|
||||||
case TypeInt:
|
|
||||||
printf(": " C_ICYAN "%zd" C_RESET, i->tok.Val.Int);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
printf(" " C_ICYAN "(unknown type)" C_RESET);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case TokIdent:
|
case TokIdent:
|
||||||
printf(C_IYELLOW "Ident" C_RESET);
|
printf(C_IYELLOW "Ident" C_RESET);
|
||||||
|
|||||||
53
tok.h
53
tok.h
@@ -2,41 +2,73 @@
|
|||||||
#define __TOK_H__
|
#define __TOK_H__
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <unistd.h>
|
|
||||||
|
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
|
||||||
typedef struct Type {
|
enum Type {
|
||||||
enum {
|
TypeVoid = 0,
|
||||||
TypeVoid = 0,
|
TypeFloat,
|
||||||
TypeFloat,
|
TypeInt,
|
||||||
TypeInt,
|
TypeBool,
|
||||||
} kind;
|
TypeChar,
|
||||||
|
TypePtr,
|
||||||
|
TypeArr,
|
||||||
|
TypeEnumSize,
|
||||||
|
};
|
||||||
|
typedef enum Type Type;
|
||||||
|
|
||||||
/*union {
|
extern size_t type_size[TypeEnumSize];
|
||||||
};*/
|
extern const char *type_str[TypeEnumSize];
|
||||||
} Type;
|
|
||||||
|
|
||||||
typedef struct Value {
|
typedef struct Value {
|
||||||
Type type;
|
Type type;
|
||||||
|
|
||||||
union {
|
union {
|
||||||
|
pseudo_void Void;
|
||||||
double Float;
|
double Float;
|
||||||
ssize_t Int;
|
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;
|
} Value;
|
||||||
|
|
||||||
|
void free_value(Value *v, bool purge);
|
||||||
|
void print_value(const Value *v, bool raw);
|
||||||
|
|
||||||
enum Operator {
|
enum Operator {
|
||||||
OpLCurl = '{',
|
OpLCurl = '{',
|
||||||
OpRCurl = '}',
|
OpRCurl = '}',
|
||||||
OpLParen = '(',
|
OpLParen = '(',
|
||||||
OpRParen = ')',
|
OpRParen = ')',
|
||||||
|
OpLBrack = '[',
|
||||||
|
OpRBrack = ']',
|
||||||
OpComma = ',',
|
OpComma = ',',
|
||||||
OpAdd = '+',
|
OpAdd = '+',
|
||||||
OpSub = '-',
|
OpSub = '-',
|
||||||
OpMul = '*',
|
OpMul = '*',
|
||||||
OpDiv = '/',
|
OpDiv = '/',
|
||||||
|
OpNot = '!',
|
||||||
|
OpAddrOf = '&',
|
||||||
OpBeginNonchars = 256,
|
OpBeginNonchars = 256,
|
||||||
|
OpEq,
|
||||||
|
OpNeq,
|
||||||
|
OpLt,
|
||||||
|
OpGt,
|
||||||
|
OpLe,
|
||||||
|
OpGe,
|
||||||
|
OpAnd,
|
||||||
|
OpOr,
|
||||||
OpNewLn,
|
OpNewLn,
|
||||||
OpEOF,
|
OpEOF,
|
||||||
OperatorEnumSize,
|
OperatorEnumSize,
|
||||||
@@ -69,6 +101,7 @@ typedef struct Tok {
|
|||||||
TokAssign,
|
TokAssign,
|
||||||
TokDeclare,
|
TokDeclare,
|
||||||
TokIf,
|
TokIf,
|
||||||
|
TokElse,
|
||||||
TokWhile,
|
TokWhile,
|
||||||
TokKindEnumSize,
|
TokKindEnumSize,
|
||||||
} kind;
|
} kind;
|
||||||
|
|||||||
58
util.c
58
util.c
@@ -2,6 +2,23 @@
|
|||||||
|
|
||||||
#include <stdarg.h>
|
#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];
|
char errbuf[ERRSZ];
|
||||||
bool err;
|
bool err;
|
||||||
size_t err_ln, err_col;
|
size_t err_ln, err_col;
|
||||||
@@ -49,8 +66,28 @@ void set_err(const char *fmt, ...) {
|
|||||||
va_end(va);
|
va_end(va);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define XMALLOC_ERR "Failed to allocate %zu bytes: Out of memory\n"
|
||||||
|
|
||||||
|
void *xmalloc(size_t size) {
|
||||||
|
void *ret = malloc(size);
|
||||||
|
if (!ret) {
|
||||||
|
fprintf(stderr, XMALLOC_ERR, size);
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *xrealloc(void *ptr, size_t size) {
|
||||||
|
void *ret = realloc(ptr, size);
|
||||||
|
if (!ret) {
|
||||||
|
fprintf(stderr, XMALLOC_ERR, size);
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
Pool *pool_new(size_t init_cap) {
|
Pool *pool_new(size_t init_cap) {
|
||||||
Pool *p = malloc(sizeof(Pool) + init_cap);
|
Pool *p = xmalloc(sizeof(Pool) + init_cap);
|
||||||
p->len = 0;
|
p->len = 0;
|
||||||
p->cap = init_cap;
|
p->cap = init_cap;
|
||||||
p->data = p + 1;
|
p->data = p + 1;
|
||||||
@@ -82,7 +119,7 @@ void *pool_alloc(Pool *p, size_t bytes) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
char *sndup(const char *s, size_t n) {
|
char *sndup(const char *s, size_t n) {
|
||||||
char *ret = malloc(n+1);
|
char *ret = xmalloc(n+1);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
memcpy(ret, s, n);
|
memcpy(ret, s, n);
|
||||||
ret[n] = 0;
|
ret[n] = 0;
|
||||||
@@ -148,6 +185,23 @@ double stod(const char *s, size_t n, ssize_t *endpos) {
|
|||||||
return res;
|
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) {
|
char *mreadfile(FILE *fp) {
|
||||||
if (fseek(fp, 0l, SEEK_END) == -1)
|
if (fseek(fp, 0l, SEEK_END) == -1)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|||||||
21
util.h
21
util.h
@@ -6,7 +6,15 @@
|
|||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.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 */
|
/* some ANSI color codes */
|
||||||
#define C_RED "\x1b[31m"
|
#define C_RED "\x1b[31m"
|
||||||
@@ -27,20 +35,27 @@
|
|||||||
|
|
||||||
#define C_RESET "\x1b[m"
|
#define C_RESET "\x1b[m"
|
||||||
|
|
||||||
|
void sleep_secs(double secs);
|
||||||
|
|
||||||
#define ERRSZ 4096
|
#define ERRSZ 4096
|
||||||
extern char errbuf[ERRSZ];
|
extern char errbuf[ERRSZ];
|
||||||
extern bool err;
|
extern bool err;
|
||||||
extern size_t err_ln, err_col;
|
extern size_t err_ln, err_col;
|
||||||
#define TRY(expr) {expr; if (err) return;}
|
#define TRY(expr) {expr; if (err) return;}
|
||||||
|
#define TRY_ELSE(expr, onerr) {expr; if (err) {onerr; return;}}
|
||||||
#define TRY_RET(expr, ret) {expr; if (err) return (ret);}
|
#define TRY_RET(expr, ret) {expr; if (err) return (ret);}
|
||||||
|
#define TRY_RET_ELSE(expr, ret, onerr) {expr; if (err) {onerr; return (ret);}}
|
||||||
void set_err(const char *fmt, ...);
|
void set_err(const char *fmt, ...);
|
||||||
|
|
||||||
#define ASSERT_UNREACHED() { fprintf(stderr, "Illegal code position reached in %s:%d\n", __FILE__, __LINE__); exit(1); }
|
#define ASSERT_UNREACHED() { fprintf(stderr, "Illegal code position reached in %s:%d\n", __FILE__, __LINE__); abort(); }
|
||||||
|
|
||||||
#define IS_NUM(c) (c >= '0' && c <= '9')
|
#define IS_NUM(c) (c >= '0' && c <= '9')
|
||||||
#define IS_ALPHA(c) ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || c == '_')
|
#define IS_ALPHA(c) ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || c == '_')
|
||||||
#define IS_ALNUM(c) (IS_ALPHA(c) || IS_NUM(c))
|
#define IS_ALNUM(c) (IS_ALPHA(c) || IS_NUM(c))
|
||||||
|
|
||||||
|
void *xmalloc(size_t size);
|
||||||
|
void *xrealloc(void *ptr, size_t size);
|
||||||
|
|
||||||
/* Useful for efficiently allocating lots of data that can all be freed at once afterwards. */
|
/* Useful for efficiently allocating lots of data that can all be freed at once afterwards. */
|
||||||
typedef struct Pool {
|
typedef struct Pool {
|
||||||
struct Pool *next;
|
struct Pool *next;
|
||||||
@@ -62,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 */);
|
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 */
|
/* convert a non-null-terminated string to a double */
|
||||||
double stod(const char *s, size_t n, ssize_t *endpos /* -1 on success */);
|
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 */
|
/* sets errno on failure */
|
||||||
char *mreadfile(FILE *fp);
|
char *mreadfile(FILE *fp);
|
||||||
|
|||||||
219
vm.c
219
vm.c
@@ -1 +1,220 @@
|
|||||||
#include "vm.h"
|
#include "vm.h"
|
||||||
|
|
||||||
|
#include "runtime.h"
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
|
#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) {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static Value *irparam_to_val(Stack *s, IRParam *v) {
|
||||||
|
if (v->kind == IRParamLiteral)
|
||||||
|
return &v->Literal;
|
||||||
|
else if (v->kind == IRParamAddr)
|
||||||
|
return &s->mem[v->Addr];
|
||||||
|
else
|
||||||
|
ASSERT_UNREACHED();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void stack_assign(Stack *s, size_t idx, const Value *v) {
|
||||||
|
stack_fit(s, idx);
|
||||||
|
if (s->holds_value[idx])
|
||||||
|
free_value(&s->mem[idx], false); /* free any overwritten heap-allocated values */
|
||||||
|
s->mem[idx] = *v;
|
||||||
|
s->holds_value[idx] = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void run(IRList *ir, const BuiltinFunc *builtin_funcs) {
|
||||||
|
/* 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 (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: {
|
||||||
|
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: {
|
||||||
|
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:
|
||||||
|
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))) {
|
||||||
|
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 (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 < n_args; i++)
|
||||||
|
fn_args[i] = *irparam_to_val(&s, &instr->CallI.args[i]);
|
||||||
|
|
||||||
|
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->next;
|
||||||
|
}
|
||||||
|
stack_term(&s);
|
||||||
|
free(fn_args);
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user