374 lines
8 KiB
C
374 lines
8 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);
|
|
|
|
if (!Str_equal(proc->name, Sl("main")))
|
|
emit_datatype(cgc, proc->type->proc.rettype);
|
|
else /* C main entry point special case */
|
|
fputs("int", cgc->cgctx->out);
|
|
|
|
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);
|
|
fputs((char *)expr->op.s, 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);
|
|
}
|