summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCarson Fleming <cflems@cflems.net>2026-03-26 16:01:27 -0700
committerCarson Fleming <cflems@cflems.net>2026-03-26 16:01:27 -0700
commit2e4f713ede25fb6147571858779fde542144c76f (patch)
treeff7989e0f5a985d7368feeb1d0f0a26142c5d7da
parent7d9fb2c733c8c64f6f74eefa0eea35b36be102cd (diff)
downloadccc-2e4f713ede25fb6147571858779fde542144c76f.tar.gz
start codegen work
-rw-r--r--ast.c12
-rw-r--r--ast.h8
-rw-r--r--codegen.c108
-rw-r--r--parser.c106
4 files changed, 172 insertions, 62 deletions
diff --git a/ast.c b/ast.c
index 3bcc637..ac95413 100644
--- a/ast.c
+++ b/ast.c
@@ -52,12 +52,6 @@ static void expr_destroy(struct expr_node* node) {
case EXPR_EMPTY:
case EXPR_INT_LIT:
break;
- case EXPR_VAR_DECL:
- var_decl_destroy(&node->as._var_decl);
- break;
- case EXPR_RETURN:
- return_destroy(&node->as._return);
- break;
}
}
@@ -66,6 +60,12 @@ static void stmt_destroy(struct stmt_node* node) {
case STMT_EXPR:
expr_destroy(&node->as._expr);
break;
+ case STMT_VAR_DECL:
+ var_decl_destroy(&node->as._var_decl);
+ break;
+ case STMT_RETURN:
+ return_destroy(&node->as._return);
+ break;
case STMT_GROUP:
group_destroy(&node->as._group);
}
diff --git a/ast.h b/ast.h
index c82f089..b058487 100644
--- a/ast.h
+++ b/ast.h
@@ -37,13 +37,9 @@ struct int_lit_node {
struct expr_node {
enum {
EXPR_EMPTY,
- EXPR_VAR_DECL,
- EXPR_RETURN,
EXPR_INT_LIT,
} type;
union {
- struct var_decl_node _var_decl;
- struct return_node _return;
struct int_lit_node _int_lit;
} as;
};
@@ -51,10 +47,14 @@ struct expr_node {
struct stmt_node {
enum {
STMT_EXPR,
+ STMT_VAR_DECL,
+ STMT_RETURN,
STMT_GROUP,
} type;
union {
struct expr_node _expr;
+ struct var_decl_node _var_decl;
+ struct return_node _return;
struct group_node _group;
} as;
diff --git a/codegen.c b/codegen.c
new file mode 100644
index 0000000..37c7a91
--- /dev/null
+++ b/codegen.c
@@ -0,0 +1,108 @@
+#include "ccc.h"
+#include "ast.h"
+#include <stdlib.h>
+#include <stdio.h>
+
+static void emit_stmt(FILE* outfile, const struct stmt_node* node);
+static void emit_expr(
+ FILE* outfile,
+ const struct expr_node* node,
+ const char* reg);
+
+static void emit_int_lit(
+ FILE* outfile,
+ const struct int_lit_node* node,
+ const char* reg
+) {
+ fprintf(outfile, "\tmov %s, %lld\n", reg, node->val);
+}
+
+static void emit_expr(
+ FILE* outfile,
+ const struct expr_node* node,
+ const char* reg
+) {
+ switch (node->type) {
+ case EXPR_EMPTY:
+ break;
+ case EXPR_INT_LIT:
+ emit_int_lit(outfile, &node->as._int_lit, reg);
+ break;
+ }
+}
+
+static void emit_var_decl(FILE* outfile, const struct var_decl_node* node) {
+ /* TODO: make do smth */
+}
+
+static void emit_return(FILE* outfile, const struct return_node* node) {
+ if (node->ret_val != NULL) emit_expr(outfile, node->ret_val, "rax");
+ fprintf(outfile, "\tret\n");
+}
+
+static void emit_group(FILE* outfile, const struct group_node* node) {
+ const struct stmt_node* body_node = node->body_head;
+ while (body_node != NULL) {
+ emit_stmt(outfile, body_node);
+ body_node = body_node->next;
+ }
+}
+
+static void emit_stmt(FILE* outfile, const struct stmt_node* node) {
+ switch (node->type) {
+ case STMT_VAR_DECL:
+ emit_var_decl(outfile, &node->as._var_decl);
+ break;
+ case STMT_RETURN:
+ emit_return(outfile, &node->as._return);
+ break;
+ case STMT_EXPR:
+ emit_expr(outfile, &node->as._expr, NULL);
+ break;
+ case STMT_GROUP:
+ emit_group(outfile, &node->as._group);
+ break;
+ }
+}
+
+static void emit_fn_decl(FILE* outfile, const struct fn_decl_node* node) {
+ fprintf(outfile, "%s:\n", node->name);
+ /* TODO: probably something to map the args to temporaries lol */
+ emit_group(outfile, &node->body);
+}
+
+static void emit_root_node(FILE* outfile, const struct root_node* node) {
+ switch (node->type) {
+ case ROOT_FN_DECL:
+ emit_fn_decl(outfile, &node->as._fn_decl);
+ break;
+ }
+}
+
+void emit_code(const struct root_node* ast, const char* path) {
+ FILE* outfile = fopen(path, "w");
+ if (outfile == NULL) CCC_PANIC;
+
+ fprintf(outfile, "section .text\n");
+
+ /* output all non-static function declarations as globals */
+ const struct root_node* node = ast;
+ while (node != NULL) {
+ if (node->type == ROOT_FN_DECL)
+ fprintf(outfile, "global %s\n", node->as._fn_decl.name);
+
+ node = node->next;
+ }
+
+ fprintf(outfile, "\n");
+
+ /* actual code body */
+ node = ast;
+ while (node != NULL) {
+ emit_root_node(outfile, node);
+ fprintf(outfile, "\n");
+ node = node->next;
+ }
+
+ fclose(outfile);
+}
diff --git a/parser.c b/parser.c
index 590ed2b..7de4ce9 100644
--- a/parser.c
+++ b/parser.c
@@ -48,6 +48,42 @@ static void parse_type(struct type_node* p_node) {
p_node->type = tok.data.ident;
}
+static void parse_return(struct return_node* p_node) {
+ expect(TK_IDENT);
+ if (strcmp(tok.data.ident, "return") != 0)
+ PARSER_PANIC("unexpected token %s; expected: return", tok.data.ident);
+
+ if (!lexer_peek(&tok))
+ PARSER_PANIC("unexpected EOF in return statement");
+
+ if (tok.type == TK_SEMI) {
+ p_node->ret_val = NULL;
+ return;
+ }
+
+ p_node->ret_val = protected_alloc(sizeof(struct expr_node));
+ handle_expr(p_node->ret_val);
+}
+
+static void parse_int_lit(struct int_lit_node* p_node) {
+ expect(TK_INT_LIT);
+ p_node->val = tok.data.int_lit;
+}
+
+static void handle_expr(struct expr_node* p_node) {
+ switch (tok.type) {
+ case TK_SEMI:
+ p_node->type = EXPR_EMPTY;
+ return;
+ case TK_INT_LIT:
+ p_node->type = EXPR_INT_LIT;
+ parse_int_lit(&p_node->as._int_lit);
+ return;
+ default:
+ PARSER_PANIC("expected expression");
+ }
+}
+
static void parse_var_decl(struct var_decl_node* p_node) {
parse_type(&p_node->type);
expect(TK_IDENT);
@@ -73,14 +109,25 @@ static void parse_group(struct group_node* p_node) {
}
static void handle_stmt(struct stmt_node* p_node) {
- if (tok.type == TK_LCURLY) {
- p_node->type = STMT_GROUP;
- parse_group(&p_node->as._group);
- } else {
- p_node->type = STMT_EXPR;
- handle_expr(&p_node->as._expr);
- expect(TK_SEMI);
+ switch (tok.type) {
+ case TK_LCURLY:
+ p_node->type = STMT_GROUP;
+ parse_group(&p_node->as._group);
+ return;
+ case TK_IDENT:
+ if (strcmp(tok.data.ident, "return") == 0) {
+ p_node->type = STMT_RETURN;
+ parse_return(&p_node->as._return);
+ } else {
+ p_node->type = STMT_VAR_DECL;
+ parse_var_decl(&p_node->as._var_decl);
+ }
+ break;
+ default:
+ p_node->type = STMT_EXPR;
+ handle_expr(&p_node->as._expr);
}
+ expect(TK_SEMI);
}
static void parse_arg_list(struct var_decl_node** pp_arg) {
@@ -116,51 +163,6 @@ static void parse_fn_decl(struct fn_decl_node* p_node) {
parse_group(&p_node->body);
}
-static void parse_return(struct return_node* p_node) {
- expect(TK_IDENT);
- if (strcmp(tok.data.ident, "return") != 0)
- PARSER_PANIC("unexpected token %s; expected: return", tok.data.ident);
-
- if (!lexer_peek(&tok))
- PARSER_PANIC("unexpected EOF in return statement");
-
- if (tok.type == TK_SEMI) {
- p_node->ret_val = NULL;
- return;
- }
-
- p_node->ret_val = protected_alloc(sizeof(struct expr_node));
- handle_expr(p_node->ret_val);
-}
-
-static void parse_int_lit(struct int_lit_node* p_node) {
- expect(TK_INT_LIT);
- p_node->val = tok.data.int_lit;
-}
-
-static void handle_expr(struct expr_node* p_node) {
- switch (tok.type) {
- case TK_SEMI:
- p_node->type = EXPR_EMPTY;
- return;
- case TK_IDENT:
- if (strcmp(tok.data.ident, "return") == 0) {
- p_node->type = EXPR_RETURN;
- parse_return(&p_node->as._return);
- } else {
- p_node->type = EXPR_VAR_DECL;
- parse_var_decl(&p_node->as._var_decl);
- }
- return;
- case TK_INT_LIT:
- p_node->type = EXPR_INT_LIT;
- parse_int_lit(&p_node->as._int_lit);
- return;
- default:
- PARSER_PANIC("expected expression");
- }
-}
-
static bool parse_root(struct root_node* p_node) {
if (!lexer_peek(&tok)) return false;