#include "all.h" static FILE *outfp; static void pri(const char *fmt, ...); static void gentype(const struct type *ty) { assert(ty); switch (ty->t) { case TYvoid: pri("void"); break; case TYbool: pri("bool"); break; case TYint: pri("%sint%z_t", ty->int_signed ? "" : "u", 8 * ty->size); break; case TYfloat: pri("%s", ty->size == 4 ? "float" : "double"); break; case TYptr: pri("%t *", ty->child); break; case TYarr: assert(ty->length >= 0); case TYslice: assert(ty->_cname); pri("%s", ty->_cname); return; case TYfn: assert(ty->_cname); pri("%s", ty->_cname); return; } if (ty->konst) pri(" const"); } static void pristring(const char *s, u64 n) { extern int isprint(int); pri("\""); for (int i = 0; i < n; ++i) { if (isprint(s[i])) pri("%c", s[i]); else fprintf(outfp, "\\%.3o", s[i]); } pri("\""); } static void genexpr(struct expr *expr); void pri(const char *fmt, ...) { va_list ap; unsigned ch; const char *S; int ws; va_start(ap, fmt); for (char c; (c = *fmt++);) { if (c != '%') { fputc(c, outfp); continue; } switch ((c = *fmt++)) { case '%': fputc(c, outfp); break; case 'c': ch = bswap32(va_arg(ap, int)); while (ch) { if (ch & 0xFF) fputc(ch, outfp); ch >>= 8; } break; case 'd': fprintf(outfp, "%d", va_arg(ap, int)); break; case 'U': fprintf(outfp, "%llu", (unsigned long long)va_arg(ap, u64)); break; case 'z': fprintf(outfp, "%zu", va_arg(ap, size_t)); break; case 'f': fprintf(outfp, "%.20f", va_arg(ap, double)); break; case 's': fprintf(outfp, "%s", va_arg(ap, const char *)); break; case 'w': ws = va_arg(ap, int); for (int i = 0; i < ws; ++i) fprintf(outfp, " "); break; case 'S': S = va_arg(ap, const char *); pristring(S, va_arg(ap, u64)); break; case 't': gentype(va_arg(ap, const struct type *)); break; case 'e': genexpr(va_arg(ap, struct expr *)); break; default: fprintf(outfp, "pri(): bad fmt '%%%c'\n", c); abort(); break; } } va_end(ap); } static void genblock(struct blockstmt block); static void genexpr(struct expr *ex) { const struct type *ty = ex->ty; switch (ex->t) { case Eintlit: if (ty == ty_int) pri("%U", ex->i); else pri("((%t)%U)", ty, ex->i); break; case Eflolit: pri("%f%s", ex->f, ty->size == 4 ? "f" : ""); break; case Estrlit: pri("%S", ex->strlit.d, ex->strlit.n); break; case Eboolit: pri("((_Bool)%U)", ex->i); break; case Enullit: pri("NULL"); break; case Ename: if (ex->ref->t == Dfn && ex->ref->_cname && *ex->ref->_cname) pri("%s", *ex->ref->_cname); else pri("%s", ex->ref->name); break; case Eprefix: pri("%c(%e)", ex->unop.op, ex->unop.child); break; case Epostfix: pri("(%e)%c", ex->unop.op, ex->unop.child); break; case Ebinop: pri("(%e %c %e)", ex->binop.lhs, ex->binop.op, ex->binop.rhs); break; case Econd: pri("(%e) ? (%e) : (%e)", ex->cond.test, ex->cond.t, ex->cond.f); break; case Ecall: pri("%e(", ex->call.callee); for (int i = 0; i < ex->call.args.n; ++i) { pri("%e", &ex->call.args.d[i]); if (i < ex->call.args.n - 1) pri(", "); } pri(")"); break; case Eindex: pri("%e[%e]", ex->index.lhs, ex->index.rhs); break; } } static void genfn(const char *cname, struct fn *fn); static void genstmt(struct stmt *stmt) { switch (stmt->t) { case Sblock: genblock(stmt->block); break; case Sexpr: genexpr(&stmt->expr); pri(";\n"); break; case Sdecl: ; struct decl decl = stmt->decl; switch (decl.t) { case Dvar: pri("%t %s", decl.var.ty, decl.name); if (decl.var.ini) pri(" = %e", decl.var.ini); pri(";\n"); break; case Dfn: if (decl.externp) genfn(decl.fn.name, &decl.fn); break; case Dtype: case Dmacro: break; } break; case Sifelse: pri("if (%e) ", &stmt->ifelse.test); genblock(stmt->ifelse.t); if (stmt->ifelse.f) { pri("else "); genstmt(stmt->ifelse.f); } break; case Swhile: pri("while (%e) ", &stmt->loop.test); genblock(stmt->loop.body); break; case Sfor: pri("for ("); if (stmt->loop.ini) genstmt(stmt->loop.ini); pri("%e;", &stmt->loop.test); if (stmt->loop.next) pri(" %e", &stmt->loop.next); pri(")"); genblock(stmt->loop.body); case Siswitch: break; case Sreturn: pri("return"); if (stmt->retex) pri(" %e", stmt->retex); pri(";\n"); break; } } static void genblock(struct blockstmt block) { pri("{\n"); for (int i = 0; i < block.stmts.n; ++i) genstmt(&block.stmts.d[i]); pri("}\n"); } static void liftnestedex(struct expr *expr) { // TODO implement when statement expressions exist //switch (expr->t) { //} } #define blocktostmt(Block) \ &(struct stmt) {Sblock, .block = Block} static void // lift nested fns and static vars liftnested(struct stmt *stmt) { static int id; if (!stmt) return; switch (stmt->t) { case Sblock: for (int i = 0; i < stmt->block.stmts.n; ++i) liftnested(&stmt->block.stmts.d[i]); break; case Sexpr: liftnestedex(&stmt->expr); break; case Sdecl: switch (stmt->decl.t) { case Dfn: if (stmt->decl.fn.body) { *stmt->decl._cname = xasprintf("__f%s_%d", stmt->decl.fn.name, id++); genfn(*stmt->decl._cname, &stmt->decl.fn); } break; case Dvar: liftnestedex(stmt->decl.var.ini); break; default: break; } break; case Sifelse: liftnested(stmt->ifelse.f); liftnested(blocktostmt(stmt->ifelse.t)); break; case Swhile: liftnested(blocktostmt(stmt->loop.body)); break; case Sfor: liftnested(stmt->loop.ini); liftnestedex(&stmt->loop.test); liftnestedex(stmt->loop.next); liftnested(blocktostmt(stmt->loop.body)); break; case Siswitch: liftnestedex(&stmt->iswitch.test); for (int i = 0; i < stmt->iswitch.cs.n; ++i) { // not visiting constant exprs liftnested(blocktostmt(stmt->iswitch.cs.d[i].t)); } if (stmt->iswitch.f) liftnested(blocktostmt(*stmt->iswitch.f)); break; case Sreturn: liftnestedex(stmt->retex); break; } } static void genfn(const char *cname, struct fn *fn) { liftnested(fn->body); if (fn->body) pri("\n"); pri("%t %s(", fn->retty, cname); for (int i = 0; i < fn->params.n; ++i) { pri("%t %s", fn->params.d[i].ty, fn->params.d[i].name); if (i < fn->params.n - 1 || fn->variadic) pri(", "); } if (fn->variadic) pri("..."); pri(")"); if (!fn->body) { pri(";\n"); } else { genblock(fn->body->block); } } static void gendecl(struct decl *decl, bool toplevel) { switch (decl->t) { case Dfn: if (!decl->externp) pri("static "); if (decl->fn.body) assert(toplevel); if (!*decl->_cname) *decl->_cname = (char *)decl->fn.name; genfn(*decl->_cname, &decl->fn); break; case Dvar: assert(!toplevel); break; case Dtype: case Dmacro: break; } } static void defctype(const struct type *ty, void *_) { static int id; char **cname = (char **)&ty->_cname; if (!ty->_cname) switch (ty->t) { case TYvoid: case TYbool: case TYint: case TYfloat: case TYptr: return; case TYarr: assert(ty->length >= 0); defctype(ty->child, NULL); *cname = xasprintf("__ty%d", id++); pri("typedef %t %s[%U];\n", ty->child, *cname, ty->length); break; case TYslice: defctype(ty->child, NULL); *cname = xasprintf("__ty%d", id++); pri("typedef struct { %t *ptr; size_t len; } %s;\n", ty->child, *cname); break; case TYfn: *cname = xasprintf("__ty%d", id++); defctype(ty->fn.retty, NULL); for (int i = 0; i < ty->fn.params.n; ++i) defctype(ty->fn.params.d[i], NULL); pri("typedef %t %s(", ty->fn.retty, *cname); for (int i = 0; i < ty->fn.params.n; ++i) { pri("%t", ty->fn.params.d[i]); if (i < ty->fn.params.n - 1 || ty->fn.variadic) pri(", "); } if (ty->fn.variadic) pri("..."); pri(");\n"); break; } } static void prelude() { pri("#include \n" "#include \n"); } void cgen(FILE *fp, const struct transunit *tu) { outfp = fp; prelude(); visittypes(defctype, NULL); for (int i = 0; i < tu->decls.n; ++i) { gendecl(&tu->decls.d[i], 1); } }