aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authortomsmeding <tom.smeding@gmail.com>2018-01-07 20:35:36 +0100
committertomsmeding <tom.smeding@gmail.com>2018-01-07 20:35:36 +0100
commit20d898496b7371872bfd128a895d39465f13c970 (patch)
tree44ab01e31b408a1ff96f454445a6886b41c0f681
parent6eb88017ce9a2e91d90634cbb77097dd821b8626 (diff)
Now with type checking
-rw-r--r--compiler.c197
-rw-r--r--node.c2
-rw-r--r--node.h2
3 files changed, 156 insertions, 45 deletions
diff --git a/compiler.c b/compiler.c
index b0297f9..adcca46 100644
--- a/compiler.c
+++ b/compiler.c
@@ -29,14 +29,31 @@ static bool oper_is_comparison(enum operator oper) {
oper == OP_LEQ || oper == OP_GEQ;
}
-static struct ref compile_expr(struct ir *ir, struct symtab *symtab, struct node *node);
+
+struct info {
+ const char *name;
+ struct type *rettype;
+};
+
+static struct ref compile_expr(struct ir *ir, struct symtab *symtab, struct info info, struct node *node);
static void compile_boolexpr(
- struct ir *ir, struct symtab *symtab, struct node *node, const char *truedest, const char *falsedest) {
+ struct ir *ir, struct symtab *symtab, struct info info, struct node *node,
+ const char *truedest, const char *falsedest) {
if (node->type == N_BINOP && oper_is_comparison(node->oper)) {
- struct ref r1 = compile_expr(ir, symtab, node->child1);
- struct ref r2 = compile_expr(ir, symtab, node->child2);
+ struct ref r1 = compile_expr(ir, symtab, info, node->child1);
+ struct ref r2 = compile_expr(ir, symtab, info, node->child2);
+ if (node->child1->valuetype != node->child2->valuetype ||
+ (node->child1->valuetype->tag != T_INT && node->child1->valuetype->tag != T_PTR) ||
+ (node->child2->valuetype->tag != T_INT && node->child2->valuetype->tag != T_PTR)) {
+ fprintf(stderr, "Invalid types ");
+ type_print(node->child1->valuetype, stderr);
+ fprintf(stderr, " and ");
+ type_print(node->child2->valuetype, stderr);
+ fprintf(stderr, " to operator %s\n", oper_string(node->oper));
+ exit(1);
+ }
ir_append(ir, irins_make_12(INS_CMP, r1, r2));
enum condcode condcode;
switch (node->oper) {
@@ -52,29 +69,30 @@ static void compile_boolexpr(
ir_append(ir, irins_make_name(INS_JMP, truedest));
} else if (node->type == N_BINOP && node->oper == OP_AND) {
const char *try2lbl = gen_label_name();
- compile_boolexpr(ir, symtab, node->child1, try2lbl, falsedest);
+ compile_boolexpr(ir, symtab, info, node->child1, try2lbl, falsedest);
ir_append(ir, irins_make_name(INS_LBL, try2lbl));
- compile_boolexpr(ir, symtab, node->child2, truedest, falsedest);
+ compile_boolexpr(ir, symtab, info, node->child2, truedest, falsedest);
} else if (node->type == N_BINOP && node->oper == OP_OR) {
const char *try2lbl = gen_label_name();
- compile_boolexpr(ir, symtab, node->child1, truedest, try2lbl);
+ compile_boolexpr(ir, symtab, info, node->child1, truedest, try2lbl);
ir_append(ir, irins_make_name(INS_LBL, try2lbl));
- compile_boolexpr(ir, symtab, node->child2, truedest, falsedest);
+ compile_boolexpr(ir, symtab, info, node->child2, truedest, falsedest);
} else if (node->type == N_UNOP && node->oper == OP_NOT) {
- compile_boolexpr(ir, symtab, node->child1, falsedest, truedest);
+ compile_boolexpr(ir, symtab, info, node->child1, falsedest, truedest);
} else {
- struct ref ref = compile_expr(ir, symtab, node);
+ struct ref ref = compile_expr(ir, symtab, info, node);
ir_append(ir, irins_make_12(INS_TEST, ref, ref));
ir_append(ir, irins_make_jcc(falsedest, CCZ));
ir_append(ir, irins_make_name(INS_JMP, truedest));
}
}
-static struct ref compile_expr(struct ir *ir, struct symtab *symtab, struct node *node) {
+static struct ref compile_expr(struct ir *ir, struct symtab *symtab, struct info info, struct node *node) {
switch (node->type) {
case N_NUM: {
struct ref ref = ref_next_register();
ir_append(ir, irins_make_01(INS_MOV, ref, ref_imm(node->value)));
+ node->valuetype = type_int(16);
return ref;
}
@@ -88,14 +106,23 @@ static struct ref compile_expr(struct ir *ir, struct symtab *symtab, struct node
fprintf(stderr, "Cannot use function name in expression\n");
exit(1);
}
+ node->valuetype = sym->rtype;
return sym->ref;
}
case N_BINOP: {
if (oper_is_basic_arith(node->oper)) {
struct ref r0 = ref_next_register();
- struct ref r1 = compile_expr(ir, symtab, node->child1);
- struct ref r2 = compile_expr(ir, symtab, node->child2);
+ struct ref r1 = compile_expr(ir, symtab, info, node->child1);
+ struct ref r2 = compile_expr(ir, symtab, info, node->child2);
+ if (node->child1->valuetype != type_int(16) || node->child2->valuetype != type_int(16)) {
+ fprintf(stderr, "Invalid operand types ");
+ type_print(node->child1->valuetype, stderr);
+ fprintf(stderr, " and ");
+ type_print(node->child2->valuetype, stderr);
+ fprintf(stderr, " to arithmetic operator %s\n", oper_string(node->oper));
+ exit(1);
+ }
enum instype instype;
switch (node->oper) {
case OP_ADD: instype = INS_ADD; break;
@@ -106,12 +133,13 @@ static struct ref compile_expr(struct ir *ir, struct symtab *symtab, struct node
default: assert(false);
}
ir_append(ir, irins_make_012(instype, r0, r1, r2));
+ node->valuetype = node->child1->valuetype;
return r0;
} else if (oper_is_comparison(node->oper) || node->oper == OP_AND || node->oper == OP_OR) {
const char *truelbl = gen_label_name();
const char *falselbl = gen_label_name();
const char *afterlbl = gen_label_name();
- compile_boolexpr(ir, symtab, node, truelbl, falselbl);
+ compile_boolexpr(ir, symtab, info, node, truelbl, falselbl);
struct ref cval = ref_next_register();
ir_append(ir, irins_make_name(INS_LBL, truelbl));
ir_append(ir, irins_make_01(INS_MOV, cval, ref_imm(1)));
@@ -123,9 +151,18 @@ static struct ref compile_expr(struct ir *ir, struct symtab *symtab, struct node
} else if (node->oper == OP_ASSIGN) {
if (node->child1->type == N_VAR ||
(node->child1->type == N_UNOP && node->child1->oper == OP_DEREF)) {
- struct ref r0 = compile_expr(ir, symtab, node->child1);
- struct ref r1 = compile_expr(ir, symtab, node->child2);
+ struct ref r0 = compile_expr(ir, symtab, info, node->child1);
+ struct ref r1 = compile_expr(ir, symtab, info, node->child2);
+ if (node->child1->valuetype != node->child2->valuetype) {
+ fprintf(stderr, "Cannot assign type ");
+ type_print(node->child2->valuetype, stderr);
+ fprintf(stderr, " to destination of type ");
+ type_print(node->child1->valuetype, stderr);
+ fprintf(stderr, "\n");
+ exit(1);
+ }
ir_append(ir, irins_make_01(INS_MOV, r0, r1));
+ node->valuetype = node->child1->valuetype;
return r0;
} else {
fprintf(stderr, "Invalid left-hand side in assignment.\n");
@@ -140,25 +177,45 @@ static struct ref compile_expr(struct ir *ir, struct symtab *symtab, struct node
switch (node->oper) {
case OP_NEG: {
struct ref r0 = ref_next_register();
- struct ref r1 = compile_expr(ir, symtab, node->child1);
+ struct ref r1 = compile_expr(ir, symtab, info, node->child1);
+ if (node->child1->valuetype != type_int(16)) {
+ fprintf(stderr, "Cannot negate value of type ");
+ type_print(node->child1->valuetype, stderr);
+ fprintf(stderr, "\n");
+ exit(1);
+ }
ir_append(ir, irins_make_01(INS_NEG, r0, r1));
+ node->valuetype = node->child1->valuetype;
return r0;
}
case OP_NOT: {
struct ref r0 = ref_next_register();
- struct ref r1 = compile_expr(ir, symtab, node->child1);
+ struct ref r1 = compile_expr(ir, symtab, info, node->child1);
+ if (node->child1->valuetype->tag != T_INT && node->child1->valuetype->tag != T_PTR) {
+ fprintf(stderr, "Cannot apply OP_NOT to value of type ");
+ type_print(node->child1->valuetype, stderr);
+ fprintf(stderr, "\n");
+ exit(1);
+ }
const char *afterlbl = gen_label_name();
ir_append(ir, irins_make_12(INS_TEST, r1, r1));
ir_append(ir, irins_make_01(INS_MOV, r0, ref_imm(0)));
ir_append(ir, irins_make_jcc(afterlbl, CCNZ));
ir_append(ir, irins_make_01(INS_MOV, r0, ref_imm(1)));
ir_append(ir, irins_make_name(INS_LBL, afterlbl));
+ node->valuetype = type_int(16);
return r0;
}
case OP_DEREF: {
- struct ref addr = compile_expr(ir, symtab, node->child1);
+ struct ref addr = compile_expr(ir, symtab, info, node->child1);
+ if (node->child1->valuetype->tag != T_PTR) {
+ fprintf(stderr, "Cannot dereference value of type ");
+ type_print(node->child1->valuetype, stderr);
+ fprintf(stderr, "\n");
+ exit(1);
+ }
struct ref r1;
switch (addr.type) {
case REF_REG: r1 = ref_mem(addr.reg, 0, REFREL_ZERO); break;
@@ -172,6 +229,8 @@ static struct ref compile_expr(struct ir *ir, struct symtab *symtab, struct node
}
struct ref r0 = ref_next_register();
ir_append(ir, irins_make_01(INS_MOV, r0, r1));
+ assert(node->child1->valuetype->tag == T_PTR);
+ node->valuetype = node->child1->valuetype->target;
return r0;
}
@@ -191,17 +250,27 @@ static struct ref compile_expr(struct ir *ir, struct symtab *symtab, struct node
exit(1);
}
if (sym->stype != ST_FUNC) {
- fprintf(stderr, "Cannot call variable\n");
+ fprintf(stderr, "Cannot call non-function\n");
exit(1);
}
- if (node_list_length(node->child1) != sym->numparams) {
- fprintf(stderr, "Invalid number of parameters to function '%s' (typechecker, come on)\n", node->name);
+ int actual_numparams = node_list_length(node->child1);
+ if (actual_numparams != sym->numparams) {
+ fprintf(stderr, "Invalid number of parameters to function '%s' (expected %d, got %d)\n",
+ node->name, sym->numparams, actual_numparams);
exit(1);
}
struct node **args = malloc(sym->numparams * sizeof(struct node*));
write_node_list(args, node->child1);
for (int i = sym->numparams - 1; i >= 0; i--) {
- struct ref r = compile_expr(ir, symtab, args[i]);
+ struct ref r = compile_expr(ir, symtab, info, args[i]);
+ if (args[i]->valuetype != sym->params[i].type) {
+ fprintf(stderr, "Invalid argument type ");
+ type_print(args[i]->valuetype, stderr);
+ fprintf(stderr, "to parameter %d (type ", i + 1);
+ type_print(sym->params[i].type, stderr);
+ fprintf(stderr, ") of function '%s'\n", node->name);
+ exit(1);
+ }
ir_append(ir, irins_make_1(INS_PUSH, r));
}
free(args);
@@ -216,6 +285,7 @@ static struct ref compile_expr(struct ir *ir, struct symtab *symtab, struct node
ir_append(ir, ins);
}
ir_append(ir, irins_make_012(INS_ADD, ref_reg(REG_SP), ref_reg(REG_SP), ref_imm(sym->numparams)));
+ node->valuetype = sym->rtype;
return retref;
}
@@ -225,11 +295,11 @@ static struct ref compile_expr(struct ir *ir, struct symtab *symtab, struct node
}
}
-static void compile_node(struct ir *ir, struct symtab *symtab, struct node *node) {
+static void compile_node(struct ir *ir, struct symtab *symtab, struct info info, struct node *node) {
switch (node->type) {
case N_LIST:
- compile_node(ir, symtab, node->child1);
- compile_node(ir, symtab, node->child2);
+ compile_node(ir, symtab, info, node->child1);
+ compile_node(ir, symtab, info, node->child2);
break;
case N_LIST_END:
@@ -237,7 +307,7 @@ static void compile_node(struct ir *ir, struct symtab *symtab, struct node *node
case N_BLOCK: {
symtab = symtab_sub(symtab);
- compile_node(ir, symtab, node->child1);
+ compile_node(ir, symtab, info, node->child1);
symtab = symtab_delete_get_parent(symtab);
break;
}
@@ -248,13 +318,29 @@ static void compile_node(struct ir *ir, struct symtab *symtab, struct node *node
fprintf(stderr, "Redeclaration of variable '%s'\n", node->name);
exit(1);
}
+
struct ref vref;
- if (node->type == N_VAR_DECL_INIT) vref = compile_expr(ir, symtab, node->child1);
- else vref = ref_imm(0);
+ if (node->type == N_VAR_DECL_INIT) {
+ struct info info2 = info;
+ info2.name = node->name;
+ vref = compile_expr(ir, symtab, info2, node->child1);
+ if (node->child1->valuetype != node->rtype) {
+ fprintf(stderr, "Invalid type in initialisation of variable '%s' (expected ", node->name);
+ type_print(node->rtype, stderr);
+ fprintf(stderr, ", got ");
+ type_print(node->child1->valuetype, stderr);
+ fprintf(stderr, ")\n");
+ exit(1);
+ }
+ } else {
+ vref = ref_imm(0);
+ }
+
struct symbol *sym = symbol_make_var(strdup(node->name), node->rtype);
if (symtab->parent == NULL) sym->ref = ir_reserve_global(ir, 1);
else sym->ref = ref_next_register();
symtab_insert(symtab, sym);
+
ir_append(ir, irins_make_01(INS_MOV, sym->ref, vref));
break;
}
@@ -282,7 +368,11 @@ static void compile_node(struct ir *ir, struct symtab *symtab, struct node *node
symtab_insert(symtab, parsym);
}
- compile_node(ir, symtab, node->child2);
+ struct info info2 = info;
+ info2.name = node->name;
+ info2.rettype = node->rtype;
+
+ compile_node(ir, symtab, info2, node->child2);
symtab = symtab_delete_get_parent(symtab);
@@ -295,12 +385,12 @@ static void compile_node(struct ir *ir, struct symtab *symtab, struct node *node
const char *thenlbl = gen_label_name();
const char *elselbl = gen_label_name();
const char *afterlbl = gen_label_name();
- compile_boolexpr(ir, symtab, node->child1, thenlbl, elselbl);
+ compile_boolexpr(ir, symtab, info, node->child1, thenlbl, elselbl);
ir_append(ir, irins_make_name(INS_LBL, thenlbl));
- compile_node(ir, symtab, node->child2);
+ compile_node(ir, symtab, info, node->child2);
ir_append(ir, irins_make_name(INS_JMP, afterlbl));
ir_append(ir, irins_make_name(INS_LBL, elselbl));
- compile_node(ir, symtab, node->child3);
+ compile_node(ir, symtab, info, node->child3);
ir_append(ir, irins_make_name(INS_LBL, afterlbl));
break;
}
@@ -310,39 +400,51 @@ static void compile_node(struct ir *ir, struct symtab *symtab, struct node *node
const char *bodylbl = gen_label_name();
const char *afterlbl = gen_label_name();
ir_append(ir, irins_make_name(INS_LBL, startlbl));
- compile_boolexpr(ir, symtab, node->child1, bodylbl, afterlbl);
+ compile_boolexpr(ir, symtab, info, node->child1, bodylbl, afterlbl);
ir_append(ir, irins_make_name(INS_LBL, bodylbl));
- compile_node(ir, symtab, node->child2);
+ compile_node(ir, symtab, info, node->child2);
ir_append(ir, irins_make_name(INS_JMP, startlbl));
ir_append(ir, irins_make_name(INS_LBL, afterlbl));
break;
}
case N_RETURN:
+ if (info.rettype != type_void()) {
+ fprintf(stderr, "Return without value from '%s'\n", info.name);
+ exit(1);
+ }
ir_append(ir, irins_make(INS_RET));
break;
case N_RETURNV: {
- struct ref ref = compile_expr(ir, symtab, node->child1);
+ struct ref ref = compile_expr(ir, symtab, info, node->child1);
+ if (node->child1->valuetype != info.rettype) {
+ fprintf(stderr, "Return with type ");
+ type_print(node->child1->valuetype, stderr);
+ fprintf(stderr, " from function '%s' with return type ", info.name);
+ type_print(info.rettype, stderr);
+ fprintf(stderr, "\n");
+ exit(1);
+ }
ir_append(ir, irins_make_1(INS_RETV, ref));
break;
}
default: // hmm, maybe it's an expression statement?
- compile_expr(ir, symtab, node);
+ compile_expr(ir, symtab, info, node);
break;
}
}
-static void compile_data_setup_node(struct ir *ir, struct symtab *symtab, struct node *node) {
+static void compile_data_setup_node(struct ir *ir, struct symtab *symtab, struct info info, struct node *node) {
switch (node->type) {
case N_LIST:
if (node->child1->type == N_VAR_DECL || node->child1->type == N_VAR_DECL_INIT) {
- compile_node(ir, symtab, node->child1);
+ compile_node(ir, symtab, info, node->child1);
node_delete_recursive(node->child1);
node->child1 = node_make_0(N_LIST_END);
}
- compile_data_setup_node(ir, symtab, node->child2);
+ compile_data_setup_node(ir, symtab, info, node->child2);
break;
case N_LIST_END:
@@ -350,7 +452,7 @@ static void compile_data_setup_node(struct ir *ir, struct symtab *symtab, struct
case N_VAR_DECL:
case N_VAR_DECL_INIT:
- compile_node(ir, symtab, node);
+ compile_node(ir, symtab, info, node);
break;
case N_FUNC_DECL:
@@ -361,13 +463,13 @@ static void compile_data_setup_node(struct ir *ir, struct symtab *symtab, struct
}
}
-static void compile_data_setup(struct ir *ir, struct symtab *symtab, struct node *root) {
+static void compile_data_setup(struct ir *ir, struct symtab *symtab, struct info info, struct node *root) {
struct ref flag = ir_reserve_global(ir, 1);
ir_append(ir, irins_make_12(INS_TEST, flag, flag));
const char *afterlbl = gen_label_name();
ir_append(ir, irins_make_jcc(afterlbl, CCNZ));
- compile_data_setup_node(ir, symtab, root);
+ compile_data_setup_node(ir, symtab, info, root);
ir_append(ir, irins_make_01(INS_MOV, flag, ref_imm(1)));
ir_append(ir, irins_make_name(INS_LBL, afterlbl));
@@ -378,7 +480,12 @@ static void compile_data_setup(struct ir *ir, struct symtab *symtab, struct node
struct ir* compile(struct node *node) {
struct ir *ir = ir_make();
struct symtab *symtab = symtab_make();
- compile_data_setup(ir, symtab, node);
- compile_node(ir, symtab, node);
+
+ struct info info;
+ info.name = "<<root>>";
+ info.rettype = type_void();
+
+ compile_data_setup(ir, symtab, info, node);
+ compile_node(ir, symtab, info, node);
return ir;
}
diff --git a/node.c b/node.c
index f34061c..3e7d945 100644
--- a/node.c
+++ b/node.c
@@ -12,6 +12,8 @@ struct node* node_make_0(enum node_type type) {
node->rtype = NULL;
node->name = NULL;
node->value = 0;
+ node->oper = -1;
+ node->valuetype = NULL;
return node;
}
diff --git a/node.h b/node.h
index f98257f..b6489d0 100644
--- a/node.h
+++ b/node.h
@@ -38,6 +38,8 @@ struct node {
char *name;
int value;
enum operator oper;
+
+ struct type *valuetype; // filled in by type checker
};