diff options
| -rw-r--r-- | ast.c | 15 | ||||
| -rw-r--r-- | ast.h | 8 | ||||
| -rw-r--r-- | codegen.c | 63 | ||||
| -rw-r--r-- | parser.c | 45 | ||||
| -rw-r--r-- | test/fibonacci.c | 4 | ||||
| -rw-r--r-- | type_checker.c | 5 |
6 files changed, 114 insertions, 26 deletions
@@ -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; } } @@ -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; @@ -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; @@ -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; } } |
