summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ast.c15
-rw-r--r--ast.h8
-rw-r--r--codegen.c63
-rw-r--r--parser.c45
-rw-r--r--test/fibonacci.c4
-rw-r--r--type_checker.c5
6 files changed, 114 insertions, 26 deletions
diff --git a/ast.c b/ast.c
index 44401cb..24a37cf 100644
--- a/ast.c
+++ b/ast.c
@@ -133,6 +133,17 @@ static void expr_destroy(struct expr_node* node) {
}
}
+static void if_destroy(struct if_node* node) {
+ expr_destroy(node->cond);
+ free(node->cond);
+ stmt_destroy(node->true_branch);
+ free(node->true_branch);
+ if (node->false_branch != NULL) {
+ stmt_destroy(node->false_branch);
+ free(node->false_branch);
+ }
+}
+
static void stmt_destroy(struct stmt_node* node) {
switch (node->type) {
case STMT_EMPTY:
@@ -148,6 +159,10 @@ static void stmt_destroy(struct stmt_node* node) {
break;
case STMT_GROUP:
group_destroy(&node->inner.group);
+ break;
+ case STMT_IF:
+ if_destroy(&node->inner.if_);
+ break;
}
}
diff --git a/ast.h b/ast.h
index 4b8f0ee..a2d576b 100644
--- a/ast.h
+++ b/ast.h
@@ -136,6 +136,12 @@ struct return_node {
struct expr_node* ret_val; /* null to return void */
};
+struct if_node {
+ struct expr_node* cond;
+ struct stmt_node* true_branch;
+ struct stmt_node* false_branch;
+};
+
struct stmt_node {
enum {
STMT_EMPTY,
@@ -143,12 +149,14 @@ struct stmt_node {
STMT_VAR_DECL,
STMT_RETURN,
STMT_GROUP,
+ STMT_IF,
} type;
union {
struct expr_node expr;
struct var_decl_node var_decl;
struct return_node return_;
struct group_node group;
+ struct if_node if_;
} inner;
struct stmt_node* next;
diff --git a/codegen.c b/codegen.c
index bdc3cbf..44fccce 100644
--- a/codegen.c
+++ b/codegen.c
@@ -34,6 +34,7 @@ static const struct storage_location MULDIV_OVERFLOW_LOC = {
static struct scope* scope;
static const struct fn_decl_node* active_fn;
+static unsigned long long branch_counter = 0;
static struct lval_def allocate_register(unsigned long long sz) {
return (struct lval_def) {
@@ -173,19 +174,33 @@ static void emit_mov(
emit_storage_loc(outfile, src, dst->sz);
break;
case STO_LABEL:
- CGEN_PANIC("can't move value into label %s", dst->loc.label);
- case STO_FN:
- CGEN_PANIC(
- "can't move value into function %s", dst->loc.decl->name);
case STO_IMM:
- CGEN_PANIC(
- "can't move value into immediate value %lld", dst->loc.value);
+ case STO_FN:
case STO_UNRESOLVED:
- CGEN_PANIC("can't move value into unresolved storage");
+ CGEN_PANIC("can't move value into storage type");
}
fprintf(outfile, "\n");
}
+static void emit_cmp_zero(FILE* outfile, const struct lval_def* lval) {
+ fprintf(outfile, "\tcmp ");
+ switch (lval->loc.type) {
+ case STO_REG:
+ emit_storage_loc(outfile, &lval->loc, lval->sz);
+ break;
+ case STO_STACK:
+ emit_size_const(outfile, lval->sz);
+ emit_storage_loc(outfile, &lval->loc, lval->sz);
+ break;
+ case STO_LABEL:
+ case STO_IMM:
+ case STO_FN:
+ case STO_UNRESOLVED:
+ CGEN_PANIC("can't compare this storage type")
+ }
+ fprintf(outfile, ", 0\n");
+}
+
/* TODO: move this utility to the type checker and use evaluated_type */
static unsigned long long get_type_size(const struct type_node* type) {
if (type->ptr_level > 0) return PTR_SIZE;
@@ -524,7 +539,7 @@ static void emit_return(FILE* outfile, const struct return_node* node) {
fprintf(outfile, "\tjmp " RETURN_LABEL_FMT "\n", active_fn->name);
}
-static void emit_group(FILE* outfile, const struct group_node* node) {
+static void emit_group_contents(FILE* outfile, const struct group_node* node) {
const struct stmt_node* body_node = node->head;
while (body_node != NULL) {
emit_stmt(outfile, body_node);
@@ -532,19 +547,40 @@ static void emit_group(FILE* outfile, const struct group_node* node) {
}
}
-static void emit_stmt_group(FILE* outfile, const struct group_node* node) {
+static void emit_group(FILE* outfile, const struct group_node* node) {
if (node->scope->next_out != scope) CGEN_PANIC("scopes are misaligned");
scope = node->scope;
scope->bp_offset = scope->next_out->bp_offset; /* don't reset bp */
- emit_group(outfile, node);
+ emit_group_contents(outfile, node);
/* don't reset sp because alloca needs to work */
scope->next_out->bp_offset = scope->bp_offset;
scope = scope->next_out;
}
+static void emit_if(FILE* outfile, const struct if_node* node) {
+ /* TODO: use type checked cond result size; using full reg size causes a segfault */
+ struct lval_def cond_result = allocate_temporary(outfile, FULL_REG_SZ);
+ emit_expr(outfile, node->cond, &cond_result);
+ emit_cmp_zero(outfile, &cond_result);
+
+ unsigned long long false_branch_num = branch_counter++;
+ fprintf(outfile, "\tjz branch%lld\n", false_branch_num);
+ emit_stmt(outfile, node->true_branch);
+
+ if (node->false_branch == NULL) {
+ fprintf(outfile, "branch%lld:\n", false_branch_num);
+ } else {
+ unsigned long long end_branch_num = branch_counter++;
+ fprintf(outfile, "\tjmp branch%lld\n", end_branch_num);
+ fprintf(outfile, "branch%lld:\n", false_branch_num);
+ emit_stmt(outfile, node->false_branch);
+ fprintf(outfile, "branch%lld:\n", end_branch_num);
+ }
+}
+
static void emit_stmt(FILE* outfile, const struct stmt_node* node) {
switch (node->type) {
case STMT_EMPTY:
@@ -559,7 +595,10 @@ static void emit_stmt(FILE* outfile, const struct stmt_node* node) {
emit_expr(outfile, &node->inner.expr, NULL);
break;
case STMT_GROUP:
- emit_stmt_group(outfile, &node->inner.group);
+ emit_group(outfile, &node->inner.group);
+ break;
+ case STMT_IF:
+ emit_if(outfile, &node->inner.if_);
break;
}
}
@@ -613,7 +652,7 @@ static void emit_fn_decl(FILE* outfile, const struct fn_decl_node* node) {
arg_decl = arg_decl->next;
}
- emit_group(outfile, &node->body);
+ emit_group_contents(outfile, &node->body);
scope = scope->next_out;
diff --git a/parser.c b/parser.c
index e078ae9..9a3e7f3 100644
--- a/parser.c
+++ b/parser.c
@@ -30,10 +30,9 @@ static void* protected_alloc(size_t sz) {
static void unexpected_token(enum token_type expected) {
/* TODO: print what token was expected */
- PARSER_PANIC("unexpected token");
+ PARSER_PANIC("unexpected token; expected %d", expected);
}
-/* TODO: reorganize the lexer to make peek cheaper */
static void peek_or_panic() {
if (!lexer_peek(&tok))
PARSER_PANIC("unexpected EOF");
@@ -311,6 +310,25 @@ static void parse_group(struct group_node* p_node) {
expect(TK_RCURLY);
}
+static void parse_if(struct if_node* p_node) {
+ expect_kw("if");
+
+ expect(TK_LPAREN);
+ p_node->cond = protected_alloc(sizeof(struct expr_node));
+ parse_expr(p_node->cond);
+ expect(TK_RPAREN);
+
+ p_node->true_branch = protected_alloc(sizeof(struct stmt_node));
+ parse_stmt(p_node->true_branch);
+
+ peek_or_panic();
+ if (tok.type != TK_IDENT || strcmp(tok.data.ident, "else") != 0) return;
+ expect_kw("else");
+
+ p_node->false_branch = protected_alloc(sizeof(struct stmt_node));
+ parse_stmt(p_node->false_branch);
+}
+
static void parse_stmt_assign(struct stmt_node* p_node) {
peek_or_panic();
if (tok.type != TK_ASSIGN) return;
@@ -346,7 +364,11 @@ static void parse_stmt(struct stmt_node* p_node) {
parse_group(&p_node->inner.group);
return;
case TK_IDENT:
- if (strcmp(tok.data.ident, "return") == 0) {
+ if (strcmp(tok.data.ident, "if") == 0) {
+ p_node->type = STMT_IF;
+ parse_if(&p_node->inner.if_);
+ return;
+ } else if (strcmp(tok.data.ident, "return") == 0) {
p_node->type = STMT_RETURN;
parse_return(&p_node->inner.return_);
break;
@@ -383,6 +405,14 @@ static void parse_fn_decl(struct fn_decl_node* p_node) {
expect(TK_IDENT);
p_node->name = tok.data.ident;
+ scope_define_var(scope, (struct var_def) {
+ .name = p_node->name,
+ .loc = {
+ .type = STO_FN,
+ .decl = p_node,
+ },
+ });
+
expect(TK_LPAREN);
scope_push(&scope);
@@ -393,15 +423,6 @@ static void parse_fn_decl(struct fn_decl_node* p_node) {
expect(TK_RPAREN);
- /* defining here makes recursion possible */
- scope_define_var(scope, (struct var_def) {
- .name = p_node->name,
- .loc = {
- .type = STO_FN,
- .decl = p_node,
- },
- });
-
parse_group(&p_node->body);
scope_pop(&scope);
diff --git a/test/fibonacci.c b/test/fibonacci.c
index c241be9..39a32be 100644
--- a/test/fibonacci.c
+++ b/test/fibonacci.c
@@ -1,6 +1,6 @@
int fib (int n) {
- if (n <= 1) return 1;
- return n * fib(n - 1);
+ if (n) return n * fib(n-1);
+ return 1;
}
int main (int argc, char** argv) {
diff --git a/type_checker.c b/type_checker.c
index d1a6c32..deb3345 100644
--- a/type_checker.c
+++ b/type_checker.c
@@ -21,6 +21,8 @@ static void type_check_var_decl(struct var_decl_node* node) {}
static void type_check_return(struct return_node* node) {}
+static void type_check_if(struct if_node* node) {}
+
static void type_check_stmt(struct stmt_node* node) {
switch (node->type) {
case STMT_EMPTY:
@@ -37,6 +39,9 @@ static void type_check_stmt(struct stmt_node* node) {
case STMT_GROUP:
type_check_group(&node->inner.group);
break;
+ case STMT_IF:
+ type_check_if(&node->inner.if_);
+ break;
}
}