From 0cabbd2a0853c332c82af621b8716643741d758a Mon Sep 17 00:00:00 2001 From: Carson Fleming Date: Tue, 21 Jul 2026 14:36:02 -0700 Subject: more faithful grammar --- ast.c | 64 ++++++++++++++++++++----------- ast.h | 43 +++++++++++---------- ccc.h | 1 - codegen.c | 67 ++++++++++++++++++--------------- makefile | 2 + parser.c | 110 ++++++++++++++++++++++++++++-------------------------- test/factorial.c | 8 ++++ test/factorial2.c | 7 ++++ test/fibonacci.c | 8 ---- test/fibonacci2.c | 8 ---- type_checker.c | 57 ++++++++++++++++++---------- 11 files changed, 211 insertions(+), 164 deletions(-) create mode 100644 test/factorial.c create mode 100644 test/factorial2.c delete mode 100644 test/fibonacci.c delete mode 100644 test/fibonacci2.c diff --git a/ast.c b/ast.c index 7c7432b..50470de 100644 --- a/ast.c +++ b/ast.c @@ -5,17 +5,16 @@ static void expr_destroy(struct expr_node* node); static void stmt_destroy(struct stmt_node* node); -static void lval_destroy(struct lval_node* node) { - switch (node->type) { - case LVAL_VAR_DECL: - break; - case LVAL_VAR_REF: - break; +static void var_decl_destroy(struct var_decl_node* node) { + if (node->initial_value != NULL) { + expr_destroy(node->initial_value); + free(node->initial_value); } } static void assign_destroy(struct assign_node* node) { - lval_destroy(&node->lval); + expr_destroy(node->lval); + free(node->lval); expr_destroy(node->rval); free(node->rval); } @@ -33,16 +32,21 @@ static void group_destroy(struct group_node* node) { free(node->scope); } -static void args_decl_destroy(struct args_decl_node* node) { - if (node != NULL) { - free(node->decl); - args_decl_destroy(node->next); +static void decl_list_destroy(struct decl_list_node* node) { + var_decl_destroy(node->decl); + free(node->decl); + + if (node->next != NULL) { + decl_list_destroy(node->next); + free(node->next); } } static void fn_decl_destroy(struct fn_decl_node* node) { - args_decl_destroy(node->args); - free(node->args); + if (node->args != NULL) { + decl_list_destroy(node->args); + free(node->args); + } group_destroy(&node->body); @@ -61,17 +65,21 @@ static void str_lit_destroy(struct str_lit_node* node) { free(node->val); } -static void args_list_destroy(struct args_eval_node* node) { - if (node != NULL) { - expr_destroy(node->expr); - free(node->expr); - args_list_destroy(node->next); +static void expr_list_destroy(struct expr_list_node* node) { + expr_destroy(node->expr); + free(node->expr); + + if (node->next != NULL) { + expr_list_destroy(node->next); + free(node->next); } } static void call_destroy(struct call_node* node) { - args_list_destroy(node->args); - free(node->args); + if (node->args != NULL) { + expr_list_destroy(node->args); + free(node->args); + } } static void unary_destroy(struct unary_node* node) { @@ -129,9 +137,22 @@ static void if_destroy(struct if_node* node) { free(node->scope); } +static void loop_init_destroy(struct loop_init_node* node) { + switch (node->type) { + case INIT_EXPR_LIST: + expr_list_destroy(node->expr_list); + free(node->expr_list); + break; + case INIT_DECL: + var_decl_destroy(node->decl); + free(node->decl); + break; + } +} + static void loop_destroy(struct loop_node* node) { if (node->init != NULL) { - expr_destroy(node->init); + loop_init_destroy(node->init); free(node->init); } if (node->cond != NULL) { @@ -158,6 +179,7 @@ static void stmt_destroy(struct stmt_node* node) { expr_destroy(&node->inner.expr); break; case STMT_VAR_DECL: + var_decl_destroy(&node->inner.var_decl); break; case STMT_RETURN: return_destroy(&node->inner.return_); diff --git a/ast.h b/ast.h index 21a4e72..a51bfea 100644 --- a/ast.h +++ b/ast.h @@ -33,35 +33,23 @@ struct var_ref_node { struct var_decl_node { struct type_ref_node type; struct var_def* def_ref; -}; - -struct lval_node { - enum { - LVAL_VAR_DECL, - LVAL_VAR_REF, - } type; - union { - struct var_ref_node var_ref; - struct var_decl_node var_decl; - } inner; - - const struct type_def* resolved_type; + struct expr_node* initial_value; }; struct assign_node { - struct lval_node lval; + struct expr_node* lval; struct expr_node* rval; }; -struct args_eval_node { +struct expr_list_node { struct expr_node* expr; - struct args_eval_node* next; + struct expr_list_node* next; }; struct call_node { - /* TODO: eventually this could also be a function pointer */ + /* TODO: function pointers */ struct fn_decl_node* called_fn_ref; /* borrowed */ - struct args_eval_node* args; + struct expr_list_node* args; }; struct unary_node { @@ -114,15 +102,15 @@ struct group_node { struct scope* scope; }; -struct args_decl_node { +struct decl_list_node { struct var_decl_node* decl; - struct args_decl_node* next; + struct decl_list_node* next; }; struct fn_decl_node { struct type_ref_node return_type; char* name; - struct args_decl_node* args; + struct decl_list_node* args; struct group_node body; struct scope* scope; @@ -140,8 +128,19 @@ struct if_node { struct scope* scope; }; +struct loop_init_node { + enum { + INIT_EXPR_LIST, + INIT_DECL, + } type; + union { + struct expr_list_node* expr_list; + struct var_decl_node* decl; + }; +}; + struct loop_node { - struct expr_node* init; /* null if absent */ + struct loop_init_node* init; /* null if absent */ struct expr_node* cond; /* null if absent */ struct expr_node* incr; /* null if absent */ struct stmt_node* body; diff --git a/ccc.h b/ccc.h index 80cbc0a..b846056 100644 --- a/ccc.h +++ b/ccc.h @@ -2,6 +2,5 @@ #define CCC_H #define CCC_PANIC { perror("ccc"); exit(1); } -#define PTR_SIZE 8 #endif diff --git a/codegen.c b/codegen.c index 955a095..a32046b 100644 --- a/codegen.c +++ b/codegen.c @@ -300,42 +300,20 @@ static void emit_var_ref( static void emit_stmt(FILE* outfile, const struct stmt_node* node); -static struct var_def* emit_var_decl( +static void emit_var_decl( FILE* outfile, const struct var_decl_node* node ) { struct lval_def var_dst = make_stack_lval(outfile, node->def_ref->resolved_type); + node->def_ref->loc = var_dst.loc; fprintf(outfile, "\t; '%s' lives in: ", node->def_ref->name); emit_storage_loc(outfile, &var_dst.loc, var_dst.type->sz); fprintf(outfile, "\n"); - struct var_def* var_def = node->def_ref; - var_def->loc = var_dst.loc; - return var_def; -} - -static struct lval_def emit_lval( - FILE* outfile, - const struct lval_node* node -) { - struct var_def* var_def; - switch (node->type) { - case LVAL_VAR_DECL: - var_def = emit_var_decl(outfile, &node->inner.var_decl); - return (struct lval_def) { - .loc = var_def->loc, - .type = var_def->resolved_type, - }; - case LVAL_VAR_REF: - var_def = node->inner.var_ref.def_ref; - return (struct lval_def) { - .loc = var_def->loc, - .type = var_def->resolved_type, - }; - } - CGEN_PANIC("unknown lval type: %d", node->type); + if (node->initial_value != NULL) + emit_expr(outfile, node->initial_value, &var_dst); } static void emit_assignment( @@ -343,7 +321,18 @@ static void emit_assignment( const struct assign_node* node, const struct lval_def* dst ) { - const struct lval_def lval_def = emit_lval(outfile, &node->lval); + struct lval_def lval_def; + switch (node->lval->type) { + case EXPR_VAR_REF: + struct var_ref_node* var_ref = &node->lval->inner.var_ref; + lval_def = (struct lval_def) { + .type = var_ref->def_ref->resolved_type, + .loc = var_ref->def_ref->loc, + }; + break; + default: + CGEN_PANIC("expression is not assignable"); + } emit_expr(outfile, node->rval, &lval_def); if (dst != NULL) emit_mov(outfile, dst, &lval_def.loc); } @@ -356,8 +345,8 @@ static void emit_call( unsigned long long orig_bp_offset = scope->bp_offset; unsigned long long arg_bp_offset = orig_bp_offset; - struct args_decl_node* arg_decl = node->called_fn_ref->args; - struct args_eval_node* arg_eval = node->args; + struct decl_list_node* arg_decl = node->called_fn_ref->args; + struct expr_list_node* arg_eval = node->args; while (arg_decl != NULL && arg_eval != NULL) { struct lval_def arg_dst = make_stack_lval(outfile, arg_decl->decl->def_ref->resolved_type); @@ -601,10 +590,26 @@ static void emit_if(FILE* outfile, const struct if_node* node) { exit_scope(node->scope, true); } +static void emit_expr_list(FILE* outfile, const struct expr_list_node* node) { + for (; node != NULL; node = node->next) + emit_expr(outfile, node->expr, NULL); +} + +static void emit_loop_init(FILE* outfile, const struct loop_init_node* node) { + switch (node->type) { + case INIT_EXPR_LIST: + emit_expr_list(outfile, node->expr_list); + break; + case INIT_DECL: + emit_var_decl(outfile, node->decl); + break; + } +} + static void emit_loop(FILE* outfile, const struct loop_node* node) { enter_scope(node->scope, scope->bp_offset); - if (node->init != NULL) emit_expr(outfile, node->init, NULL); + if (node->init != NULL) emit_loop_init(outfile, node->init); unsigned long long loop_num = ++loop_counter; if (node->cond != NULL) { @@ -671,7 +676,7 @@ static void emit_fn_decl(FILE* outfile, const struct fn_decl_node* node) { long long spilled_bp_ofs = -16; // return address + old bp unsigned char arg_regnum = 0; - struct args_decl_node* arg_decl = node->args; + struct decl_list_node* arg_decl = node->args; for (; arg_decl != NULL; arg_decl = arg_decl->next) { struct var_def* arg_def = arg_decl->decl->def_ref; struct lval_def arg_dst = diff --git a/makefile b/makefile index 9cab3aa..8d04c39 100644 --- a/makefile +++ b/makefile @@ -1,2 +1,4 @@ all: gcc -std=c23 -g -O0 -Wall -Werror *.c -o ccc +clean: + rm -f ./ccc *.s *.o *.out **/*.s **/*.o **/*.out diff --git a/parser.c b/parser.c index 28e36e3..7bad30f 100644 --- a/parser.c +++ b/parser.c @@ -124,34 +124,29 @@ static void parse_var_ref(struct var_ref_node* p_node) { } static void parse_expr_assign(struct expr_node* p_node) { - switch (p_node->type) { - case EXPR_VAR_REF: - p_node->inner.assign.lval = (struct lval_node) { - .type = LVAL_VAR_REF, - .inner.var_ref = p_node->inner.var_ref, - }; - break; - default: - PARSER_PANIC("expression is not assignable"); - } + struct expr_node* lval = protected_alloc(sizeof(struct expr_node)); + *lval = *p_node; p_node->type = EXPR_ASSIGN; - p_node->inner.assign.rval = protected_alloc(sizeof(struct expr_node)); + struct assign_node* a_node = &p_node->inner.assign; + a_node->lval = lval; expect(TK_ASSIGN); - parse_expr(p_node->inner.assign.rval); + a_node->rval = protected_alloc(sizeof(struct expr_node)); + parse_expr(a_node->rval); } -static void parse_args_eval(struct args_eval_node** pp_arg) { +static void parse_expr_list(struct expr_list_node* p_node) { for (;;) { - *pp_arg = protected_alloc(sizeof(struct args_eval_node)); - (*pp_arg)->expr = protected_alloc(sizeof(struct expr_node)); - parse_expr((*pp_arg)->expr); - pp_arg = &((*pp_arg)->next); + p_node->expr = protected_alloc(sizeof(struct expr_node)); + parse_expr(p_node->expr); peek_or_panic(); - if (tok.type == TK_RPAREN) break; + if (tok.type != TK_COMMA) break; expect(TK_COMMA); + + p_node->next = protected_alloc(sizeof(struct expr_list_node)); + p_node = p_node->next; } } @@ -174,7 +169,11 @@ static void parse_expr_call(struct expr_node* p_node) { expect(TK_LPAREN); peek_or_panic(); - if (tok.type != TK_RPAREN) parse_args_eval(&p_node->inner.call.args); + if (tok.type != TK_RPAREN) { + p_node->inner.call.args = + protected_alloc(sizeof(struct expr_list_node)); + parse_expr_list(p_node->inner.call.args); + } expect(TK_RPAREN); } @@ -286,6 +285,13 @@ static void parse_var_decl(struct var_decl_node* p_node) { }); if (p_node->def_ref == NULL) PARSER_PANIC("redefinition of '%s'", tok.data.ident); + + peek_or_panic(); + if (tok.type == TK_ASSIGN) { + expect(TK_ASSIGN); + p_node->initial_value = protected_alloc(sizeof(struct expr_node)); + parse_expr(p_node->initial_value); + } } static void parse_stmt(struct stmt_node* p_node); @@ -366,6 +372,25 @@ static void parse_while(struct loop_node* p_node) { scope_pop(&scope); } +static void parse_loop_init(struct loop_init_node* p_node) { + peek_or_panic(); + if (tok.type != TK_IDENT + || !scope_get_type(scope, NULL, &(struct type_key) { + .name = tok.data.ident, + .how_long = 0, + .marked_signed = false, + .marked_unsigned = false, + })) { + p_node->type = INIT_EXPR_LIST; + p_node->expr_list = protected_alloc(sizeof(struct expr_list_node)); + parse_expr_list(p_node->expr_list); + } else { + p_node->type = INIT_DECL; + p_node->decl = protected_alloc(sizeof(struct var_decl_node)); + parse_var_decl(p_node->decl); + } +} + static void parse_for(struct loop_node* p_node) { expect_kw("for"); @@ -375,8 +400,8 @@ static void parse_for(struct loop_node* p_node) { expect(TK_LPAREN); peek_or_panic(); if (tok.type != TK_SEMI) { - p_node->init = protected_alloc(sizeof(struct expr_node)); - parse_expr(p_node->init); + p_node->init = protected_alloc(sizeof(struct loop_init_node)); + parse_loop_init(p_node->init); } expect(TK_SEMI); peek_or_panic(); @@ -398,30 +423,6 @@ static void parse_for(struct loop_node* p_node) { scope_pop(&scope); } -static void parse_stmt_assign(struct stmt_node* p_node) { - peek_or_panic(); - if (tok.type != TK_ASSIGN) return; - - switch (p_node->type) { - case STMT_VAR_DECL: - p_node->inner.expr.inner.assign.lval = (struct lval_node) { - .type = LVAL_VAR_DECL, - .inner.var_decl = p_node->inner.var_decl, - }; - break; - default: - return; - } - - p_node->type = STMT_EXPR; - p_node->inner.expr.type = EXPR_ASSIGN; - p_node->inner.expr.inner.assign.rval = - protected_alloc(sizeof(struct expr_node)); - - expect(TK_ASSIGN); - parse_expr(p_node->inner.expr.inner.assign.rval); -} - static void parse_stmt(struct stmt_node* p_node) { peek_or_panic(); switch (tok.type) { @@ -467,20 +468,20 @@ static void parse_stmt(struct stmt_node* p_node) { parse_expr(&p_node->inner.expr); } - parse_stmt_assign(p_node); expect(TK_SEMI); } -static void parse_args_decl(struct args_decl_node** pp_decl) { +static void parse_decl_list(struct decl_list_node* p_node) { for (;;) { - *pp_decl = protected_alloc(sizeof(struct args_decl_node)); - (*pp_decl)->decl = protected_alloc(sizeof(struct var_decl_node)); - parse_var_decl((*pp_decl)->decl); - pp_decl = &((*pp_decl)->next); + p_node->decl = protected_alloc(sizeof(struct var_decl_node)); + parse_var_decl(p_node->decl); peek_or_panic(); - if (tok.type == TK_RPAREN) break; + if (tok.type != TK_COMMA) break; expect(TK_COMMA); + + p_node->next = protected_alloc(sizeof(struct decl_list_node)); + p_node = p_node->next; } } @@ -509,7 +510,10 @@ static void parse_fn_decl(struct fn_decl_node* p_node) { p_node->scope = scope; peek_or_panic(); - if (tok.type != TK_RPAREN) parse_args_decl(&p_node->args); + if (tok.type != TK_RPAREN) { + p_node->args = protected_alloc(sizeof(struct decl_list_node)); + parse_decl_list(p_node->args); + } expect(TK_RPAREN); diff --git a/test/factorial.c b/test/factorial.c new file mode 100644 index 0000000..0525b8f --- /dev/null +++ b/test/factorial.c @@ -0,0 +1,8 @@ +int fact (int n) { + if (n) return n * fact(n-1); + else return 1; +} + +int main (int argc, char** argv) { + return fact(argc); +} diff --git a/test/factorial2.c b/test/factorial2.c new file mode 100644 index 0000000..867b709 --- /dev/null +++ b/test/factorial2.c @@ -0,0 +1,7 @@ +int main (int argc, char** argv) { + int result = 1; + for (int n = argc; n; n = n - 1) { + result = result * n; + } + return result; +} diff --git a/test/fibonacci.c b/test/fibonacci.c deleted file mode 100644 index a07c111..0000000 --- a/test/fibonacci.c +++ /dev/null @@ -1,8 +0,0 @@ -int fib (int n) { - if (n) return n * fib(n-1); - else return 1; -} - -int main (int argc, char** argv) { - return fib(argc); -} diff --git a/test/fibonacci2.c b/test/fibonacci2.c deleted file mode 100644 index b8adb9a..0000000 --- a/test/fibonacci2.c +++ /dev/null @@ -1,8 +0,0 @@ -int main (int argc, char** argv) { - int n = argc; - int result = 1; - for (; n; n = n - 1) { - result = result * n; - } - return result; -} diff --git a/type_checker.c b/type_checker.c index 8a07f2a..5905c83 100644 --- a/type_checker.c +++ b/type_checker.c @@ -15,7 +15,6 @@ static struct ast* ast_ref; static struct scope* scope; static void type_check_expr(struct expr_node* node); -static void type_check_lval(struct lval_node* node); static void type_check_stmt(struct stmt_node* node); static void type_check_group(struct group_node* node); @@ -78,13 +77,25 @@ static const struct type_def* resolve_var_ref(struct var_ref_node* node) { } static void type_check_var_decl(struct var_decl_node* node) { - node->def_ref->resolved_type = resolve_type_ref(&node->type); + const struct type_def* lval_type = resolve_type_ref(&node->type); + node->def_ref->resolved_type = lval_type; + if (node->initial_value != NULL) { + type_check_expr(node->initial_value); + assert_cast_compatible(lval_type, node->initial_value->resolved_type); + } } static const struct type_def* resolve_assign(struct assign_node* node) { - type_check_lval(&node->lval); + switch (node->lval->type) { + case EXPR_VAR_REF: + break; + default: + TYPE_PANIC("expression is not assignable"); + } + + type_check_expr(node->lval); type_check_expr(node->rval); - const struct type_def* lval_type = node->lval.resolved_type; + const struct type_def* lval_type = node->lval->resolved_type; const struct type_def* rval_type = node->rval->resolved_type; assert_cast_compatible(lval_type, rval_type); @@ -92,9 +103,12 @@ static const struct type_def* resolve_assign(struct assign_node* node) { } static const struct type_def* resolve_call(struct call_node* node) { - struct args_decl_node* arg_decl = node->called_fn_ref->args; - struct args_eval_node* arg_eval = node->args; + struct decl_list_node* arg_decl = node->called_fn_ref->args; + struct expr_list_node* arg_eval = node->args; while (arg_decl != NULL && arg_eval != NULL) { + if (arg_decl->decl->initial_value != NULL) + TYPE_PANIC("C does not support default arguments"); + type_check_var_decl(arg_decl->decl); const struct type_def* decl_type = arg_decl->decl->def_ref->resolved_type; @@ -127,18 +141,6 @@ static const struct type_def* resolve_binary(struct binary_node* node) { return node->lhs->resolved_type; } -static void type_check_lval(struct lval_node* node) { - switch (node->type) { - case LVAL_VAR_DECL: - type_check_var_decl(&node->inner.var_decl); - node->resolved_type = node->inner.var_decl.def_ref->resolved_type; - break; - case LVAL_VAR_REF: - node->resolved_type = resolve_var_ref(&node->inner.var_ref); - break; - } -} - static void type_check_expr(struct expr_node* node) { switch (node->type) { case EXPR_INT_LIT: @@ -185,10 +187,25 @@ static void type_check_if(struct if_node* node) { exit_scope(node->scope); } +static void type_check_expr_list(struct expr_list_node* node) { + for (; node != NULL; node = node->next) type_check_expr(node->expr); +} + +static void type_check_loop_init(struct loop_init_node* node) { + switch (node->type) { + case INIT_EXPR_LIST: + type_check_expr_list(node->expr_list); + break; + case INIT_DECL: + type_check_var_decl(node->decl); + break; + } +} + static void type_check_loop(struct loop_node* node) { enter_scope(node->scope); - if (node->init != NULL) type_check_expr(node->init); + if (node->init != NULL) type_check_loop_init(node->init); if (node->cond != NULL) type_check_expr(node->cond); if (node->incr != NULL) type_check_expr(node->incr); type_check_stmt(node->body); @@ -238,7 +255,7 @@ static void type_check_fn_decl(struct fn_decl_node* node) { node->resolved_return_type = resolve_type_ref(&node->return_type); - for (struct args_decl_node* arg_decl = node->args; + for (struct decl_list_node* arg_decl = node->args; arg_decl != NULL; arg_decl = arg_decl->next) type_check_var_decl(arg_decl->decl); -- cgit v1.2.3