summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCarson Fleming <cflems@cflems.net>2026-03-28 09:42:44 -1000
committerCarson Fleming <cflems@cflems.net>2026-03-28 09:42:44 -1000
commit33d10c0a684eaacb59102e2e2c2494ef54113aa1 (patch)
tree230b1f1cfc4a6cde3285e2fac35c7f9a7a414e17
parent55929c155f929a886a0fe72c1c16a7913830350a (diff)
downloadccc-33d10c0a684eaacb59102e2e2c2494ef54113aa1.tar.gz
we got assignments bois
-rw-r--r--ast.c23
-rw-r--r--ast.h6
-rw-r--r--codegen.c115
-rw-r--r--parser.c82
-rw-r--r--test/assign.c5
5 files changed, 190 insertions, 41 deletions
diff --git a/ast.c b/ast.c
index 1334073..05f2755 100644
--- a/ast.c
+++ b/ast.c
@@ -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;
diff --git a/ast.h b/ast.h
index e5354ae..6b7c3d5 100644
--- a/ast.h
+++ b/ast.h
@@ -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,
diff --git a/codegen.c b/codegen.c
index 6dfaf4a..c8a9933 100644
--- a/codegen.c
+++ b/codegen.c
@@ -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;
diff --git a/parser.c b/parser.c
index d4af5a0..8166ea5 100644
--- a/parser.c
+++ b/parser.c
@@ -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;
+}