#include #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); }