start with c codegen
authorMart Lubbers <mart@martlubbers.net>
Wed, 10 Feb 2021 08:56:11 +0000 (09:56 +0100)
committerMart Lubbers <mart@martlubbers.net>
Wed, 10 Feb 2021 08:56:11 +0000 (09:56 +0100)
.gitignore
Makefile
ast.c
ast.h
gen.c [deleted file]
gen.h [deleted file]
genc.c [new file with mode: 0644]
genc.h [new file with mode: 0644]
splc.c
util.c
util.h

index d56d33c..6992eb8 100644 (file)
@@ -3,3 +3,4 @@ parse.[ch]
 scan.[ch]
 *.o
 y.output
+a.c
index 245e79f..749b3fd 100644 (file)
--- 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 (file)
--- 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; i<stmt->data.swhile.nbody; i++) {
+               for (int i = 0; i<stmt->data.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 (file)
--- 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 (file)
index ab6b6d3..0000000
--- a/gen.c
+++ /dev/null
@@ -1,9 +0,0 @@
-#include <stdbool.h>
-
-#include "ast.h"
-
-bool gen(struct ast *res)
-{
-       (void)res;
-       return true;
-}
diff --git a/gen.h b/gen.h
deleted file mode 100644 (file)
index ca695a4..0000000
--- a/gen.h
+++ /dev/null
@@ -1,10 +0,0 @@
-#ifndef GEN_H
-#define GEN_H
-
-#include <stdbool.h>
-
-#include "ast.h"
-
-bool gen(struct ast *res);
-
-#endif
diff --git a/genc.c b/genc.c
new file mode 100644 (file)
index 0000000..10f3d4f
--- /dev/null
+++ b/genc.c
@@ -0,0 +1,255 @@
+#include <stdbool.h>
+
+#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; i<expr->data.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; i<expr->data.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; i<expr->data.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; i<expr->data.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; i<stmt->data.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; i<stmt->data.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; i<stmt->data.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; i<stmt->data.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; i<decl->data.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; i<decl->data.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; i<decl->data.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 <stdint.h>\n"
+               "#include <stdlib.h>\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; i<ast->ndecls && 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 (file)
index 0000000..37e772d
--- /dev/null
+++ b/genc.h
@@ -0,0 +1,11 @@
+#ifndef GENC_H
+#define GENC_H
+
+#include <stdbool.h>
+#include <stdio.h>
+
+#include "ast.h"
+
+int genc(struct ast *res, FILE *cout);
+
+#endif
diff --git a/splc.c b/splc.c
index 6b23b49..f4f6682 100644 (file)
--- a/splc.c
+++ b/splc.c
@@ -3,7 +3,7 @@
 #include <getopt.h>
 
 #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 (file)
--- 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 (file)
--- a/util.h
+++ b/util.h
@@ -3,6 +3,7 @@
 
 #include <stdarg.h>
 #include <stdbool.h>
+#include <stdio.h>
 
 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