From 3094a11313a6248611c69b2cdbfa2b7b9dcfc399 Mon Sep 17 00:00:00 2001 From: Mart Lubbers Date: Wed, 10 Feb 2021 09:56:11 +0100 Subject: [PATCH] start with c codegen --- .gitignore | 1 + Makefile | 4 +- ast.c | 9 +- ast.h | 3 + gen.c | 9 -- gen.h | 10 --- genc.c | 255 +++++++++++++++++++++++++++++++++++++++++++++++++++++ genc.h | 11 +++ splc.c | 60 +++++++++---- util.c | 14 +++ util.h | 3 + 11 files changed, 337 insertions(+), 42 deletions(-) delete mode 100644 gen.c delete mode 100644 gen.h create mode 100644 genc.c create mode 100644 genc.h diff --git a/.gitignore b/.gitignore index d56d33c..6992eb8 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ parse.[ch] scan.[ch] *.o y.output +a.c diff --git a/Makefile b/Makefile index 245e79f..749b3fd 100644 --- a/Makefile +++ b/Makefile @@ -2,7 +2,7 @@ CFLAGS+=-Wall -Wextra -std=c99 -pedantic -D_XOPEN_SOURCE=700 -ggdb YFLAGS+=-d --locations -v --defines=parse.h LFLAGS+=--header-file=scan.h -OBJECTS:=scan.o parse.o ast.o util.o gen.o type.o +OBJECTS:=scan.o parse.o ast.o util.o type.o genc.o all: splc splc: $(OBJECTS) @@ -11,4 +11,4 @@ parse.h: parse.c expr.c: y.tab.h clean: - $(RM) $(OBJECTS) y.output parse.h scan.h scan.c parse.c expr + $(RM) $(OBJECTS) y.output parse.h scan.h scan.c parse.c expr a.c diff --git a/ast.c b/ast.c index 29141e4..2bfcf43 100644 --- a/ast.c +++ b/ast.c @@ -6,15 +6,15 @@ #include "ast.h" #include "parse.h" -static const char *binop_str[] = { +const char *binop_str[] = { [binor] = "||", [binand] = "&&", [eq] = "==", [neq] = "!=", [leq] = "<=", [le] = "<", [geq] = ">=", [ge] = ">", [cons] = ":", [plus] = "+", [minus] = "-", [times] = "*", [divide] = "/", [modulo] = "%", [power] = "^", }; -static const char *fieldspec_str[] = { +const char *fieldspec_str[] = { [fst] = "fst", [snd] = "snd", [hd] = "hd", [tl] = "tl"}; -static const char *unop_str[] = { [inverse] = "!", [negate] = "-", }; +const char *unop_str[] = { [inverse] = "!", [negate] = "-", }; static const char *basictype_str[] = { [btbool] = "Bool", [btchar] = "Char", [btint] = "Int", [btvoid] = "Void", @@ -388,9 +388,8 @@ void stmt_print(struct stmt *stmt, int indent, FILE *out) safe_fprintf(out, "while ("); expr_print(stmt->data.swhile.pred, out); safe_fprintf(out, ") {\n"); - for (int i = 0; idata.swhile.nbody; i++) { + for (int i = 0; idata.swhile.nbody; i++) stmt_print(stmt->data.swhile.body[i], indent+1, out); - } pindent(indent, out); safe_fprintf(out, "}\n"); break; diff --git a/ast.h b/ast.h index a8e98bb..b797c57 100644 --- a/ast.h +++ b/ast.h @@ -8,6 +8,9 @@ struct ast; #include "parse.h" +extern const char *fieldspec_str[]; +extern const char *binop_str[]; +extern const char *unop_str[]; struct ast { int ndecls; struct decl **decls; diff --git a/gen.c b/gen.c deleted file mode 100644 index ab6b6d3..0000000 --- a/gen.c +++ /dev/null @@ -1,9 +0,0 @@ -#include - -#include "ast.h" - -bool gen(struct ast *res) -{ - (void)res; - return true; -} diff --git a/gen.h b/gen.h deleted file mode 100644 index ca695a4..0000000 --- a/gen.h +++ /dev/null @@ -1,10 +0,0 @@ -#ifndef GEN_H -#define GEN_H - -#include - -#include "ast.h" - -bool gen(struct ast *res); - -#endif diff --git a/genc.c b/genc.c new file mode 100644 index 0000000..10f3d4f --- /dev/null +++ b/genc.c @@ -0,0 +1,255 @@ +#include + +#include "ast.h" + +#define gdie(msg) { fprintf(stderr, "%s", msg); return 1; } + +int expr_genc(struct expr *expr, FILE *cout); + +void binop_genc(char *fun, struct expr *l, struct expr *r, FILE *cout) +{ + safe_fprintf(cout, "%s(", fun); + expr_genc(l, cout); + safe_fprintf(cout, ", "); + expr_genc(r, cout); + safe_fprintf(cout, ")"); +} + +int expr_genc(struct expr *expr, FILE *cout) +{ + int r = 0; + char buf[] = "\\x55"; + if (expr == NULL) + return 0; + switch(expr->type) { + case ebinop: + if (expr->type == ebinop && expr->data.ebinop.op == cons) { + binop_genc("splc_cons", expr->data.ebinop.l, + expr->data.ebinop.r, cout); + } else if (expr->type == ebinop && expr->data.ebinop.op == power) { + binop_genc("splc_power", expr->data.ebinop.l, + expr->data.ebinop.r, cout); + } else { + safe_fprintf(cout, "("); + expr_genc(expr->data.ebinop.l, cout); + safe_fprintf(cout, "%s", binop_str[expr->data.ebinop.op]); + expr_genc(expr->data.ebinop.r, cout); + safe_fprintf(cout, ")"); + } + break; + case ebool: + safe_fprintf(cout, "%s", expr->data.ebool ? "true" : "false"); + break; + case echar: + safe_fprintf(cout, "'%s'", + escape_char(expr->data.echar, buf, false)); + break; + case efuncall: + safe_fprintf(cout, "%s(", expr->data.efuncall.ident); + for(int i = 0; idata.efuncall.nargs; i++) { + expr_genc(expr->data.efuncall.args[i], cout); + if (i+1 < expr->data.efuncall.nargs) + safe_fprintf(cout, ", "); + } + safe_fprintf(cout, ")"); + for (int i = 0; idata.efuncall.nfields; i++) + fprintf(cout, "->%s", + fieldspec_str[expr->data.efuncall.fields[i]]); + break; + case eint: + safe_fprintf(cout, "%d", expr->data.eint); + break; + case eident: + fprintf(cout, "%s", expr->data.eident.ident); + for (int i = 0; idata.eident.nfields; i++) + fprintf(cout, "->%s", + fieldspec_str[expr->data.eident.fields[i]]); + break; + case enil: + safe_fprintf(cout, "NULL"); + break; + case etuple: + binop_genc("splc_tuple", expr->data.etuple.left, + expr->data.etuple.right, cout); + break; + case estring: + safe_fprintf(cout, "\""); + for (int i = 0; idata.estring.nchars; i++) + safe_fprintf(cout, "%s", escape_char( + expr->data.estring.chars[i], buf, true)); + safe_fprintf(cout, "\""); + break; + case eunop: + safe_fprintf(cout, "(%s", unop_str[expr->data.eunop.op]); + expr_genc(expr->data.eunop.l, cout); + safe_fprintf(cout, ")"); + break; + default: + die("Unknown expression node\n"); + } + return r; +} + +int type_genc(struct type *type, FILE *cout) +{ + if (type == NULL) { + fprintf(cout, "WORD "); + } else { + switch(type->type) { + case tbasic: + fprintf(cout, "WORD "); + break; + case tlist: + fprintf(cout, "struct splc_list *"); + break; + case ttuple: + fprintf(cout, "struct splc_tuple *"); + break; + case tvar: + fprintf(cout, "WORD "); + break; + default: + die("Unsupported type node\n"); + } + } + return 0; +} + +int vardecl_genc(struct vardecl *vardecl, int indent, FILE *cout) +{ + int r = 0; + if (vardecl == NULL) + return 0; + pindent(indent, cout); + type_genc(vardecl->type, cout); + fprintf(cout, "%s = ", vardecl->ident); + r = expr_genc(vardecl->expr, cout); + fprintf(cout, ";\n"); + return r; +} + +void stmt_genc(struct stmt *stmt, int indent, FILE *cout) +{ + if (stmt == NULL) + return; + switch(stmt->type) { + case sassign: + pindent(indent, cout); + fprintf(cout, "%s", stmt->data.sassign.ident); + for (int i = 0; idata.sassign.nfields; i++) + fprintf(cout, "->%s", stmt->data.sassign.fields[i]); + safe_fprintf(cout, " = "); + expr_genc(stmt->data.sassign.expr, cout); + safe_fprintf(cout, ";\n"); + break; + case sif: + pindent(indent, cout); + safe_fprintf(cout, "if ("); + expr_genc(stmt->data.sif.pred, cout); + safe_fprintf(cout, ") {\n"); + for (int i = 0; idata.sif.nthen; i++) + stmt_genc(stmt->data.sif.then[i], indent+1, cout); + pindent(indent, cout); + safe_fprintf(cout, "} else {\n"); + for (int i = 0; idata.sif.nels; i++) + stmt_genc(stmt->data.sif.els[i], indent+1, cout); + pindent(indent, cout); + safe_fprintf(cout, "}\n"); + break; + case sreturn: + pindent(indent, cout); + safe_fprintf(cout, "return "); + expr_genc(stmt->data.sreturn, cout); + safe_fprintf(cout, ";\n"); + break; + case sexpr: + pindent(indent, cout); + expr_genc(stmt->data.sexpr, cout); + safe_fprintf(cout, ";\n"); + break; + case svardecl: + vardecl_genc(stmt->data.svardecl, indent, cout); + break; + case swhile: + pindent(indent, cout); + safe_fprintf(cout, "while ("); + expr_genc(stmt->data.swhile.pred, cout); + safe_fprintf(cout, ") {\n"); + for (int i = 0; idata.swhile.nbody; i++) + stmt_genc(stmt->data.swhile.body[i], indent+1, cout); + pindent(indent, cout); + safe_fprintf(cout, "}\n"); + break; + default: + die("Unsupported stmt node\n"); + } +} + +int decl_genc(struct decl *decl, FILE *cout) +{ + switch (decl->type) { + case dfundecl: + type_genc(decl->data.dfun.rtype, cout); + safe_fprintf(cout, "%s (", decl->data.dfun.ident); + for (int i = 0; idata.dfun.nargs; i++) { + if (i < decl->data.dfun.natypes) + type_genc(decl->data.dfun.atypes[i], cout); + safe_fprintf(cout, "%s", decl->data.dfun.args[i]); + if (i < decl->data.dfun.nargs - 1) + safe_fprintf(cout, ", "); + } + safe_fprintf(cout, ") /*"); + if (decl->data.dfun.rtype != NULL) { + safe_fprintf(cout, " :: "); + for (int i = 0; idata.dfun.natypes; i++) { + type_print(decl->data.dfun.atypes[i], cout); + safe_fprintf(cout, " "); + } + safe_fprintf(cout, "-> "); + type_print(decl->data.dfun.rtype, cout); + } + safe_fprintf(cout, "*/ {\n"); + for (int i = 0; idata.dfun.nbody; i++) + stmt_genc(decl->data.dfun.body[i], 1, cout); + safe_fprintf(cout, "}\n"); + break; + case dvardecl: + return vardecl_genc(decl->data.dvar, 0, cout); + } + return 0; +} + +int genc(struct ast *ast, FILE *cout) +{ + fprintf(cout, + "#include \n" + "#include \n" + "#define WORD intptr_t\n" + "struct splc_tuple { WORD fst; WORD snd; };\n" + "struct splc_list { WORD hd; struct splc_list *tl; };\n" + "struct splc_tuple *splc_tuple(WORD fst, WORD snd) {\n" + "\tstruct splc_tuple *res = malloc(sizeof(splc_tuple));\n" + "\tres->fst = fst;\n" + "\tres->snd = snd;\n" + "\treturn res;\n" + "}\n" + "struct splc_list *splc_cons(WORD hd, struct splc_list *tl) {\n" + "\tstruct splc_list *res = malloc(sizeof(splc_tuple));\n" + "\tres->hd = hd;\n" + "\tres->tl = tl;\n" + "\treturn res;\n" + "}\n" + "WORD splc_power(WORD l, WORD r) {\n" + "\tWORD res = 1;\n" + "\twhile(l-- >= 0)\n" + "\t\tres *= r;\n" + "\treturn res;\n" + "}\n" + ); + int r = 0; + for (int i = 0; indecls && r == 0; i++) { + fprintf(cout, "\n"); + r = decl_genc(ast->decls[i], cout); + } + return r; +} diff --git a/genc.h b/genc.h new file mode 100644 index 0000000..37e772d --- /dev/null +++ b/genc.h @@ -0,0 +1,11 @@ +#ifndef GENC_H +#define GENC_H + +#include +#include + +#include "ast.h" + +int genc(struct ast *res, FILE *cout); + +#endif diff --git a/splc.c b/splc.c index 6b23b49..f4f6682 100644 --- a/splc.c +++ b/splc.c @@ -3,7 +3,7 @@ #include #include "ast.h" -#include "gen.h" +#include "genc.h" #include "parse.h" #include "scan.h" #include "type.h" @@ -19,17 +19,36 @@ void usage(FILE *out, char *arg0) "Options:\n" "\t-p\tPretty print the parsed abstract syntax tree\n" "\t-t\tPretty print the typed abstract syntax tree\n" + "\t-g LANG\tGenerate LANG code (default: C)\n" + "\t \tSupported languages: C\n" + "\t-o FILE\tOutput code to FILE (default: a.suf)\n" "\t-h\tShow this help\n" , arg0); } int main(int argc, char *argv[]) { - int opt; + int opt, r; bool pparse = false, ptype = false; + char *cfile = NULL; + enum {langc} lang = langc; + const char *suffix[] = { [langc] = "c" }; + struct ast *result = NULL; + FILE *cout; - while ((opt = getopt(argc, argv, "hpt")) != -1) { + while ((opt = getopt(argc, argv, "g:ho:tp")) != -1) { switch (opt) { + case 'g': + if (strcmp(optarg, "c") == 0 + || strcmp(optarg, "C") == 0) { + lang = langc; + } else { + usage(stderr, argv[0]); + } + break; + case 'o': + cfile = safe_strdup(optarg); + break; case 'p': pparse = true; break; @@ -48,27 +67,36 @@ int main(int argc, char *argv[]) if ((yyin = fopen(argv[optind], "r")) == NULL) pdie("fopen"); - struct ast *result = NULL; - int r = yyparse(&result); - if (r != 0) - return 1; + //Parse + r = yyparse(&result); + if (yyin != stdin) + safe_fclose(yyin); yylex_destroy(); + if (r != 0) + return r; if (pparse) ast_print(result, stdout); + + //Typecheck if ((result = type(result)) == NULL) { - r = 1; - goto end; + return 1; } if (ptype) ast_print(result, stdout); - if (!gen(result)) { - r = 1; - goto end; + + //Generate code + if (cfile == NULL) + sprintf(cfile = safe_malloc(10), "a.%s", suffix[lang]); + cout = safe_fopen(cfile, "w+"); + free(cfile); + switch(lang) { + case langc: + r = genc(result, cout); + break; + default: + die("unsupported language\n"); } + safe_fclose(cout); ast_free(result); -end: - if (yyin == stdin) - if (fclose(yyin) == -1) - perror("fclose"); return r; } diff --git a/util.c b/util.c index 5103745..a3d8bcf 100644 --- a/util.c +++ b/util.c @@ -204,3 +204,17 @@ void *safe_strdup(const char *c) pdie("strdup"); return res; } + +FILE *safe_fopen(const char *path, const char *mode) +{ + FILE *res = fopen(path, mode); + if (res == NULL) + pdie("fopen"); + return res; +} + +void safe_fclose(FILE *file) +{ + if (fclose(file) == -1) + pdie("fclose"); +} diff --git a/util.h b/util.h index 79d12d8..47677a8 100644 --- a/util.h +++ b/util.h @@ -3,6 +3,7 @@ #include #include +#include struct list { void *el; struct list *tail; }; struct list *list_cons(void *el, struct list *tail); @@ -24,5 +25,7 @@ void pindent(int indent, FILE *out); void safe_fprintf(FILE *out, const char *msg, ...); void *safe_malloc(size_t size); void *safe_strdup(const char *c); +FILE *safe_fopen(const char *path, const char *mode); +void safe_fclose(FILE *file); #endif -- 2.20.1