rutile/compiler/cgC.c
2025-01-21 00:20:21 -03:00

370 lines
7.9 KiB
C

#include <stdio.h>
#include "pre.h"
#include "codegen.h"
#include "cgC.h"
#include "ast.h"
#include "libs/stb_ds.h"
#define EMIT_SEMICOLON_NL(out) fputs(";\n", out)
#define EMIT_RB_NL(out) fputs("}\n", out)
static void
emit_expr(CodegenC *cgc, const Ast *expr);
static void
emit_expr_list(CodegenC *cgc, const Vec(Ast *) exprs, bool sep);
static void
emit_node(CodegenC *cgc, const Ast *node);
static void
indent(CodegenC *cgc)
{
#define INDENT(out) fputc('\t', out)
switch (cgc->indent) {
case 8: INDENT(cgc->cgctx->out); /* fallthrough */
case 7: INDENT(cgc->cgctx->out); /* fallthrough */
case 6: INDENT(cgc->cgctx->out); /* fallthrough */
case 5: INDENT(cgc->cgctx->out); /* fallthrough */
case 4: INDENT(cgc->cgctx->out); /* fallthrough */
case 3: INDENT(cgc->cgctx->out); /* fallthrough */
case 2: INDENT(cgc->cgctx->out); /* fallthrough */
case 1: INDENT(cgc->cgctx->out); /* fallthrough */
case 0: break;
default:
for (isize left = 0; left < cgc->indent; ++left)
INDENT(cgc->cgctx->out);
}
#undef INDENT
}
static void
emit_comment(CodegenC *cgc, Str comment, bool nl_after)
{
fprintf(cgc->cgctx->out, "/* %s */%c", comment.s, nl_after ? '\n' : '\0');
}
static void
emit_include(CodegenC *cgc, Str path, bool local)
{
fprintf(
cgc->cgctx->out, "#include %c%s%c\n",
local ? '"' : '<', path.s, local ? '"' : '>'
);
}
static const char *
basic_datatype_to_c(CodegenC *cgc, const DataType *dt)
{
switch (dt->kind) {
case DtkBasic:
switch (dt->size) {
case 0: return "void";
case 1: return "uint8_t";
case 2: return "uint16_t";
case 4: return "uint32_t";
case 8: return "uint64_t";
}
break;
case DtkVoid:
return "void";
break;
}
return nil;
}
static void
emit_datatype(CodegenC *cgc, const DataType *dt)
{
switch (dt->kind) {
case DtkBasic:
case DtkVoid:
fputs(basic_datatype_to_c(cgc, dt), cgc->cgctx->out);
break;
case DtkStruct:
fprintf(cgc->cgctx->out, "struct %s", dt->name.s);
break;
}
}
static void
emit_c_attribute(CodegenC *cgc, Str attr)
{
fprintf(cgc->cgctx->out, "__attribute((%s))", attr.s);
}
static void
emit_structdecl(CodegenC *cgc, const DataType *dt)
{
fputs("struct %s {\n", cgc->cgctx->out);
for (isize i = 0; i < arrlen(dt->compound.fields); ++i) {
emit_datatype(cgc, dt->compound.fields[i]);
EMIT_SEMICOLON_NL(cgc->cgctx->out);
}
fputc('}', cgc->cgctx->out);
if (dt->compound.packed)
emit_c_attribute(cgc, Sl("packed"));
EMIT_SEMICOLON_NL(cgc->cgctx->out);
}
static void
emit_vardecl(CodegenC *cgc, const AstVarDecl *decl)
{
if (decl->kind == SymConst)
fputs("const ", cgc->cgctx->out);
emit_datatype(cgc, decl->type);
fprintf(cgc->cgctx->out, " %s", decl->name.s);
if (decl->expr != nil) {
fputc('=', cgc->cgctx->out);
emit_expr(cgc, decl->expr);
}
EMIT_SEMICOLON_NL(cgc->cgctx->out);
}
static void
emit_varassign(CodegenC *cgc, const AstVarAssign *assign)
{
fprintf(cgc->cgctx->out, "%s = ", assign->name.s);
emit_expr(cgc, assign->expr);
EMIT_SEMICOLON_NL(cgc->cgctx->out);
}
static void
emit_proc(CodegenC *cgc, const AstProc *proc)
{
if (!proc->ispublic)
fputs("static ", cgc->cgctx->out);
emit_datatype(cgc, proc->type->proc.rettype);
fprintf(cgc->cgctx->out, " %s(", proc->name.s);
const isize arglen = arrlen(proc->args);
if (arglen == 0)
fputs("void", cgc->cgctx->out);
for (isize i = 0; i < arglen; ++i) {
AstIdentTypePair arg = proc->args[i];
//emit_datatype(cgc, arg.dtype);
fputs("uint64_t ", cgc->cgctx->out);
fputs((char *)arg.ident.s, cgc->cgctx->out);
if (i + 1 < arglen)
fputc(',', cgc->cgctx->out);
}
fputs(")\n{\n", cgc->cgctx->out);
if (proc->body != nil)
emit_node(cgc, proc->body);
EMIT_RB_NL(cgc->cgctx->out);
}
static void
emit_proccall(CodegenC *cgc, const AstProcCall *call)
{
fprintf(cgc->cgctx->out, "%s(", call->name.s);
if (call->args != nil)
emit_expr_list(cgc, (const Vec(Ast *))call->args->stmts, true);
fputs(")", cgc->cgctx->out);
}
static void
emit_if(CodegenC *cgc, const AstIf *ift)
{
fputs("if (", cgc->cgctx->out);
emit_expr(cgc, ift->cond);
fputs("){\n", cgc->cgctx->out);
emit_node(cgc, ift->true_body);
fputc('}', cgc->cgctx->out);
if (ift->false_body != nil) {
fputs("else", cgc->cgctx->out);
fputs("{\n", cgc->cgctx->out);
emit_node(cgc, ift->false_body);
fputc('}', cgc->cgctx->out);
}
fputc('\n', cgc->cgctx->out);
}
static void
emit_whileLoop(CodegenC *cgc, const AstLoop *whl)
{
fputs("while (", cgc->cgctx->out);
emit_expr(cgc, whl->precond);
fputs("){\n", cgc->cgctx->out);
emit_node(cgc, whl->body);
fputs("}\n", cgc->cgctx->out);
}
static void
emit_loop(CodegenC *cgc, const AstLoop *loop)
{
if (loop->precond != nil)
emit_whileLoop(cgc, loop);
else if (loop->postcond != nil)
unreachable();
}
static void
emit_return(CodegenC *cgc, const Ast *ret_expr)
{
fputs("return ", cgc->cgctx->out);
emit_expr(cgc, ret_expr);
EMIT_SEMICOLON_NL(cgc->cgctx->out);
}
static void
emit_break(CodegenC *cgc, const Ast *unused)
{
(void)unused;
fputs("break;\n", cgc->cgctx->out);
}
static void
emit_discard(CodegenC *cgc, const Ast *expr)
{
emit_node(cgc, expr);
}
static void
emit_expr_number(CodegenC *cgc, const AstNumber *num)
{
fprintf(cgc->cgctx->out, "%lu", num->n);
}
static void
emit_expr_strlit(CodegenC *cgc, const Str *strlit)
{
fprintf(cgc->cgctx->out, "\"%s\"", strlit->s);
}
static void
emit_expr_ident(CodegenC *cgc, const Str *ident)
{
fputs((char *)ident->s, cgc->cgctx->out);
}
static void
emit_expr_unary(CodegenC *cgc, const AstUnary *unary)
{
emit_expr(cgc, unary->atom);
}
static void
emit_expr_binop(CodegenC *cgc, const AstBinop *expr)
{
/* guard binops with parenthesis, even if they are redundant */
fputc('(', cgc->cgctx->out);
emit_expr(cgc, expr->left);
fputc('+', cgc->cgctx->out);
emit_expr(cgc, expr->right);
fputc(')', cgc->cgctx->out);
}
static void
emit_expr(CodegenC *cgc, const Ast *expr)
{
if (expr == nil)
return;
switch (expr->type) {
case AST_BINEXPR:
emit_expr_binop(cgc, &expr->bin);
break;
case AST_UNARY:
emit_expr_unary(cgc, &expr->unary);
break;
case AST_NUMBER:
emit_expr_number(cgc, &expr->number);
break;
case AST_STRLIT:
emit_expr_strlit(cgc, &expr->strlit);
break;
case AST_IDENT:
emit_expr_ident(cgc, &expr->ident);
break;
case AST_PROCCALL:
emit_proccall(cgc, &expr->call);
break;
default:
unreachable();
}
}
static void
emit_expr_list(CodegenC *cgc, const Vec(Ast *) exprs, bool sep)
{
const isize exprs_len = arrlen(exprs);
for (isize i = 0; i < exprs_len; ++i) {
emit_expr(cgc, exprs[i]);
if (sep && i + 1 < exprs_len) /* no trailling separator */
fputc(',', cgc->cgctx->out);
}
}
static void
emit_stmt_list(CodegenC *cgc, Vec(Ast *) stmts)
{
for (isize i = 0; i < arrlen(stmts); ++i) {
emit_node(cgc, stmts[i]);
}
}
static void
emit_node(CodegenC *cgc, const Ast *node)
{
switch (node->type) {
case AST_STMTS:
emit_stmt_list(cgc, node->stmts);
break;
case AST_PROCDEF:
emit_proc(cgc, &node->proc);
break;
case AST_PROCCALL:
emit_proccall(cgc, &node->call);
EMIT_SEMICOLON_NL(cgc->cgctx->out);
break;
case AST_IF:
emit_if(cgc, &node->ifse);
break;
case AST_LOOP:
emit_loop(cgc, &node->loop);
break;
case AST_RETURN:
emit_return(cgc, node->ret);
break;
case AST_BREAK:
emit_break(cgc, nil);
break;
case AST_DISCARD:
emit_discard(cgc, node->discard.expr);
break;
case AST_VARDECL:
emit_vardecl(cgc, &node->var);
break;
case AST_VARASSIGN:
emit_varassign(cgc, &node->varassgn);
break;
case AST_BINEXPR:
case AST_UNARY:
case AST_NUMBER:
case AST_STRLIT:
case AST_IDENT:
emit_expr(cgc, node);
break;
case AST_PROCCALL_ARGS:
case AST_EXPRS:
case AST_INVALID:
unreachable();
}
}
void
cgC(CodegenC *cgc, const Ast *program)
{
cgc->cgctx->out = stdout;
char note_buf[255] = {0};
snprintf(note_buf, sizeof(note_buf),
"generated C IR from %s", cgc->cgctx->cctx->current_filename.s
);
emit_comment(cgc, Str_from_c(note_buf), true);
emit_include(cgc, Sl("stdint.h"), false);
fputc('\n', cgc->cgctx->out);
emit_node(cgc, program);
}