diff options
| author | Carson Fleming <cflems@cflems.net> | 2026-03-28 09:42:44 -1000 |
|---|---|---|
| committer | Carson Fleming <cflems@cflems.net> | 2026-03-28 09:42:44 -1000 |
| commit | 33d10c0a684eaacb59102e2e2c2494ef54113aa1 (patch) | |
| tree | 230b1f1cfc4a6cde3285e2fac35c7f9a7a414e17 | |
| parent | 55929c155f929a886a0fe72c1c16a7913830350a (diff) | |
| download | ccc-33d10c0a684eaacb59102e2e2c2494ef54113aa1.tar.gz | |
we got assignments bois
| -rw-r--r-- | ast.c | 23 | ||||
| -rw-r--r-- | ast.h | 6 | ||||
| -rw-r--r-- | codegen.c | 115 | ||||
| -rw-r--r-- | parser.c | 82 | ||||
| -rw-r--r-- | test/assign.c | 5 |
5 files changed, 190 insertions, 41 deletions
@@ -18,6 +18,23 @@ static void var_ref_destroy(struct var_ref_node* node) { free(node->ident); } +static void lval_destroy(struct lval_node* node) { + switch (node->type) { + case LVAL_VAR_DECL: + var_decl_destroy(&node->as._var_decl); + break; + case LVAL_VAR_REF: + var_ref_destroy(&node->as._var_ref); + break; + } +} + +static void assign_destroy(struct assign_node* node) { + lval_destroy(&node->lval); + expr_destroy(node->rval); + free(node->rval); +} + static void group_destroy(struct group_node* node) { struct stmt_node* body_node = node->body_head; while (body_node != NULL) { @@ -53,17 +70,21 @@ static void return_destroy(struct return_node* node) { static void expr_destroy(struct expr_node* node) { switch (node->type) { - case EXPR_EMPTY: case EXPR_INT_LIT: break; case EXPR_VAR_REF: var_ref_destroy(&node->as._var_ref); break; + case EXPR_ASSIGN: + assign_destroy(&node->as._assign); + break; } } static void stmt_destroy(struct stmt_node* node) { switch (node->type) { + case STMT_EMPTY: + break; case STMT_EXPR: expr_destroy(&node->as._expr); break; @@ -30,7 +30,7 @@ struct var_decl_node { struct lval_node { enum { LVAL_VAR_DECL, - LVAL_VAR_USE, + LVAL_VAR_REF, } type; union { struct var_ref_node _var_ref; @@ -46,13 +46,14 @@ struct assign_node { struct expr_node { enum { - EXPR_EMPTY, EXPR_INT_LIT, EXPR_VAR_REF, + EXPR_ASSIGN, } type; union { struct int_lit_node _int_lit; struct var_ref_node _var_ref; + struct assign_node _assign; } as; }; @@ -73,6 +74,7 @@ struct return_node { struct stmt_node { enum { + STMT_EMPTY, STMT_EXPR, STMT_VAR_DECL, STMT_RETURN, @@ -61,23 +61,23 @@ static void emit_mov( if (src->type == REGISTER && sz < 4) { fprintf(outfile, "\tmovzx "); emit_storage_loc(outfile, dst, FULL_REG_SZ); - fprintf(outfile, ", "); - emit_storage_loc(outfile, src, sz); } else { fprintf(outfile, "\tmov "); emit_storage_loc(outfile, dst, sz); - fprintf(outfile, ", "); - emit_storage_loc(outfile, src, sz); } + + fprintf(outfile, ", "); + emit_storage_loc(outfile, src, sz); break; case BP_OFFSET: - fprintf(outfile, "\tmov "); if (src->type == BP_OFFSET) { - if (sz > 4) fprintf(outfile, "qword "); - else if (sz > 2) fprintf(outfile, "dword "); - else if (sz > 1) fprintf(outfile, "word "); - else fprintf(outfile, "byte "); + /* `mov mem, mem` is illegal in x86_64 */ + emit_mov(outfile, &RV_LOC, src, sz); + emit_mov(outfile, dst, &RV_LOC, sz); + return; } + + fprintf(outfile, "\tmov "); emit_storage_loc(outfile, dst, sz); fprintf(outfile, ", "); emit_storage_loc(outfile, src, sz); @@ -91,6 +91,12 @@ static void emit_mov( fprintf(outfile, "\n"); } +static void emit_expr( + FILE* outfile, + const struct expr_node* node, + const struct storage_location* storage +); + static void emit_int_lit( FILE* outfile, const struct int_lit_node* node, @@ -107,37 +113,24 @@ static void emit_int_lit( FULL_REG_SZ); } +static struct var_def get_var(const char* name) { + struct var_def var_def; + if (!scope_get_var(scope, &var_def, name)) + CGEN_PANIC("reference to undefined variable %s", name); + return var_def; +} + static void emit_var_ref( FILE* outfile, const struct var_ref_node* node, const struct storage_location* storage ) { if (storage != NULL) { - struct var_def var_def; - if (!scope_get_var(scope, &var_def, node->ident)) - CGEN_PANIC("reference to undefined variable %s", node->ident); - + struct var_def var_def = get_var(node->ident); emit_mov(outfile, storage, &var_def.loc, var_def.sz); } } -static void emit_expr( - FILE* outfile, - const struct expr_node* node, - const struct storage_location* storage -) { - switch (node->type) { - case EXPR_EMPTY: - break; - case EXPR_INT_LIT: - emit_int_lit(outfile, &node->as._int_lit, storage); - break; - case EXPR_VAR_REF: - emit_var_ref(outfile, &node->as._var_ref, storage); - break; - } -} - static void emit_stmt(FILE* outfile, const struct stmt_node* node); static unsigned long long get_type_size(const struct type_node* type) { @@ -150,19 +143,73 @@ static unsigned long long get_type_size(const struct type_node* type) { return type_def.size; } -static void emit_var_decl(FILE* outfile, const struct var_decl_node* node) { +static struct var_def emit_var_decl( + FILE* outfile, + const struct var_decl_node* node +) { unsigned long long type_sz = get_type_size(&node->type); fprintf(outfile, "\tsub rsp, %llu\n", type_sz); scope->bp_offset += type_sz; - scope_define_var(scope, (struct var_def) { + + struct var_def var_def = { .name = node->ident, .loc = { .type = BP_OFFSET, .offset = scope->bp_offset, }, .sz = type_sz, - }); + }; + scope_define_var(scope, var_def); + return var_def; +} + +struct lval_def { + struct storage_location loc; + unsigned long long sz; +}; + +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->as._var_decl); + return (struct lval_def) {.loc = var_def.loc, .sz = var_def.sz}; + case LVAL_VAR_REF: + var_def = get_var(node->as._var_ref.ident); + return (struct lval_def) {.loc = var_def.loc, .sz = var_def.sz}; + } + CGEN_PANIC("unknown lval type: %d", node->type); +} + +static void emit_assignment( + FILE* outfile, + const struct assign_node* node, + const struct storage_location* storage +) { + const struct lval_def lval_def = emit_lval(outfile, &node->lval); + emit_expr(outfile, node->rval, &lval_def.loc); + if (storage != NULL) emit_mov(outfile, storage, &lval_def.loc, lval_def.sz); +} + +static void emit_expr( + FILE* outfile, + const struct expr_node* node, + const struct storage_location* storage +) { + switch (node->type) { + case EXPR_INT_LIT: + emit_int_lit(outfile, &node->as._int_lit, storage); + break; + case EXPR_VAR_REF: + emit_var_ref(outfile, &node->as._var_ref, storage); + break; + case EXPR_ASSIGN: + emit_assignment(outfile, &node->as._assign, storage); + } } static void emit_return(FILE* outfile, const struct return_node* node) { @@ -193,6 +240,8 @@ static void emit_stmt_group(FILE* outfile, const struct group_node* node) { static void emit_stmt(FILE* outfile, const struct stmt_node* node) { switch (node->type) { + case STMT_EMPTY: + break; case STMT_VAR_DECL: emit_var_decl(outfile, &node->as._var_decl); break; @@ -78,6 +78,8 @@ static void parse_type(struct type_node* p_node) { } } +static void parse_expr(struct expr_node* p_node); + static void parse_int_lit(struct int_lit_node* p_node) { expect(TK_INT_LIT); p_node->val = tok.data.int_lit; @@ -88,23 +90,64 @@ static void parse_var_ref(struct var_ref_node* p_node) { p_node->ident = tok.data.ident; } +static void parse_expr_assign(struct expr_node* p_node) { + peek_or_panic(); + if (tok.type != TK_ASSIGN) return; + + switch (p_node->type) { + case EXPR_VAR_REF: + p_node->as._assign.lval = (struct lval_node) { + .type = LVAL_VAR_REF, + .as._var_ref = p_node->as._var_ref, + }; + break; + default: + PARSER_PANIC("expression is not assignable"); + } + + p_node->type = EXPR_ASSIGN; + p_node->as._assign.rval = protected_alloc(sizeof(struct expr_node)); + + expect(TK_ASSIGN); + parse_expr(p_node->as._assign.rval); +} + static void parse_expr(struct expr_node* p_node) { peek_or_panic(); switch (tok.type) { - case TK_SEMI: - p_node->type = EXPR_EMPTY; - return; + case TK_LPAREN: + expect(TK_LPAREN); + parse_expr(p_node); + expect(TK_RPAREN); + break; case TK_INT_LIT: p_node->type = EXPR_INT_LIT; parse_int_lit(&p_node->as._int_lit); - return; + break; case TK_IDENT: p_node->type = EXPR_VAR_REF; parse_var_ref(&p_node->as._var_ref); - return; + + peek_or_panic(); + if (tok.type == TK_ASSIGN) { + p_node->type = EXPR_ASSIGN; + p_node->as._assign = (struct assign_node) { + .lval = { + .type = LVAL_VAR_REF, + .as._var_ref = p_node->as._var_ref, + }, + .rval = protected_alloc(sizeof(struct expr_node)), + }; + + expect(TK_ASSIGN); + parse_expr(p_node->as._assign.rval); + } + break; default: PARSER_PANIC("expected expression"); } + + parse_expr_assign(p_node); } static void parse_var_decl(struct var_decl_node* p_node) { @@ -144,9 +187,36 @@ static void parse_group(struct group_node* p_node) { expect(TK_RCURLY); } +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->as._expr.as._assign.lval = (struct lval_node) { + .type = LVAL_VAR_DECL, + .as._var_decl = p_node->as._var_decl, + }; + break; + default: + return; + } + + p_node->type = STMT_EXPR; + p_node->as._expr.type = EXPR_ASSIGN; + p_node->as._expr.as._assign.rval = + protected_alloc(sizeof(struct expr_node)); + + expect(TK_ASSIGN); + parse_expr(p_node->as._expr.as._assign.rval); +} + static void parse_stmt(struct stmt_node* p_node) { peek_or_panic(); switch (tok.type) { + case TK_SEMI: + p_node->type = STMT_EMPTY; + break; case TK_LCURLY: p_node->type = STMT_GROUP; parse_group(&p_node->as._group); @@ -165,6 +235,8 @@ static void parse_stmt(struct stmt_node* p_node) { p_node->type = STMT_EXPR; parse_expr(&p_node->as._expr); } + + parse_stmt_assign(p_node); expect(TK_SEMI); } diff --git a/test/assign.c b/test/assign.c new file mode 100644 index 0000000..d4153b2 --- /dev/null +++ b/test/assign.c @@ -0,0 +1,5 @@ +int main(int argc, char** argv) { + int x; + int y = (x = argc); + return x; +} |
