// Copyright 2009 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. #include "a.h" /* * Translate a .goc file into a .c file. A .goc file is a combination * of a limited form of Go with C. */ /* package PACKAGENAME {# line} func NAME([NAME TYPE { , NAME TYPE }]) [(NAME TYPE { , NAME TYPE })] \{ C code with proper brace nesting \} */ /* * We generate C code which implements the function such that it can * be called from Go and executes the C code. */ static char *input; static Buf *output; #define EOF -1 static int xgetchar(void) { int c; c = *input; if(c == 0) return EOF; input++; return c; } static void xungetc(void) { input--; } static void xputchar(char c) { bwrite(output, &c, 1); } static int xisspace(int c) { return c == ' ' || c == '\t' || c == '\r' || c == '\n'; } /* Whether we're emitting for gcc */ static int gcc; /* File and line number */ static const char *file; static unsigned int lineno = 1; /* List of names and types. */ struct params { struct params *next; char *name; char *type; }; /* index into type_table */ enum { Bool, Float, Int, Uint, Uintptr, String, Slice, Eface, }; static struct { char *name; int size; } type_table[] = { /* variable sized first, for easy replacement */ /* order matches enum above */ /* default is 32-bit architecture sizes */ {"bool", 1}, {"float", 4}, {"int", 4}, {"uint", 4}, {"uintptr", 4}, {"String", 8}, {"Slice", 12}, {"Eface", 8}, /* fixed size */ {"float32", 4}, {"float64", 8}, {"byte", 1}, {"int8", 1}, {"uint8", 1}, {"int16", 2}, {"uint16", 2}, {"int32", 4}, {"uint32", 4}, {"int64", 8}, {"uint64", 8}, {nil}, }; /* Fixed structure alignment (non-gcc only) */ int structround = 4; /* Unexpected EOF. */ static void bad_eof(void) { fatal("%s:%ud: unexpected EOF\n", file, lineno); } /* Free a list of parameters. */ static void free_params(struct params *p) { while (p != nil) { struct params *next; next = p->next; xfree(p->name); xfree(p->type); xfree(p); p = next; } } /* Read a character, tracking lineno. */ static int getchar_update_lineno(void) { int c; c = xgetchar(); if (c == '\n') ++lineno; return c; } /* Read a character, giving an error on EOF, tracking lineno. */ static int getchar_no_eof(void) { int c; c = getchar_update_lineno(); if (c == EOF) bad_eof(); return c; } /* Read a character, skipping comments. */ static int getchar_skipping_comments(void) { int c; while (1) { c = getchar_update_lineno(); if (c != '/') return c; c = xgetchar(); if (c == '/') { do { c = getchar_update_lineno(); } while (c != EOF && c != '\n'); return c; } else if (c == '*') { while (1) { c = getchar_update_lineno(); if (c == EOF) return EOF; if (c == '*') { do { c = getchar_update_lineno(); } while (c == '*'); if (c == '/') break; } } } else { xungetc(); return '/'; } } } /* * Read and return a token. Tokens are string or character literals * or else delimited by whitespace or by [(),{}]. * The latter are all returned as single characters. */ static char * read_token(void) { int c, q; char *buf; unsigned int alc, off; char* delims = "(),{}"; while (1) { c = getchar_skipping_comments(); if (c == EOF) return nil; if (!xisspace(c)) break; } alc = 16; buf = xmalloc(alc + 1); off = 0; if(c == '"' || c == '\'') { q = c; buf[off] = c; ++off; while (1) { if (off+2 >= alc) { // room for c and maybe next char alc *= 2; buf = xrealloc(buf, alc + 1); } c = getchar_no_eof(); buf[off] = c; ++off; if(c == q) break; if(c == '\\') { buf[off] = getchar_no_eof(); ++off; } } } else if (xstrrchr(delims, c) != nil) { buf[off] = c; ++off; } else { while (1) { if (off >= alc) { alc *= 2; buf = xrealloc(buf, alc + 1); } buf[off] = c; ++off; c = getchar_skipping_comments(); if (c == EOF) break; if (xisspace(c) || xstrrchr(delims, c) != nil) { if (c == '\n') lineno--; xungetc(); break; } } } buf[off] = '\0'; return buf; } /* Read a token, giving an error on EOF. */ static char * read_token_no_eof(void) { char *token = read_token(); if (token == nil) bad_eof(); return token; } /* Read the package clause, and return the package name. */ static char * read_package(void) { char *token; token = read_token_no_eof(); if (token == nil) fatal("%s:%ud: no token\n", file, lineno); if (!streq(token, "package")) { fatal("%s:%ud: expected \"package\", got \"%s\"\n", file, lineno, token); } return read_token_no_eof(); } /* Read and copy preprocessor lines. */ static void read_preprocessor_lines(void) { while (1) { int c; do { c = getchar_skipping_comments(); } while (xisspace(c)); if (c != '#') { xungetc(); break; } xputchar(c); do { c = getchar_update_lineno(); xputchar(c); } while (c != '\n'); } } /* * Read a type in Go syntax and return a type in C syntax. We only * permit basic types and pointers. */ static char * read_type(void) { char *p, *op, *q; int pointer_count; unsigned int len; p = read_token_no_eof(); if (*p != '*') return p; op = p; pointer_count = 0; while (*p == '*') { ++pointer_count; ++p; } len = xstrlen(p); q = xmalloc(len + pointer_count + 1); xmemmove(q, p, len); while (pointer_count > 0) { q[len] = '*'; ++len; --pointer_count; } q[len] = '\0'; xfree(op); return q; } /* Return the size of the given type. */ static int type_size(char *p) { int i; if(p[xstrlen(p)-1] == '*') return type_table[Uintptr].size; for(i=0; type_table[i].name; i++) if(streq(type_table[i].name, p)) return type_table[i].size; fatal("%s:%ud: unknown type %s\n", file, lineno, p); return 0; } /* * Read a list of parameters. Each parameter is a name and a type. * The list ends with a ')'. We have already read the '('. */ static struct params * read_params(int *poffset) { char *token; struct params *ret, **pp, *p; int offset, size, rnd; ret = nil; pp = &ret; token = read_token_no_eof(); offset = 0; if (!streq(token, ")")) { while (1) { p = xmalloc(sizeof(struct params)); p->name = token; p->type = read_type(); p->next = nil; *pp = p; pp = &p->next; size = type_size(p->type); rnd = size; if(rnd > structround) rnd = structround; if(offset%rnd) offset += rnd - offset%rnd; offset += size; token = read_token_no_eof(); if (!streq(token, ",")) break; token = read_token_no_eof(); } } if (!streq(token, ")")) { fatal("%s:%ud: expected '('\n", file, lineno); } if (poffset != nil) *poffset = offset; return ret; } /* * Read a function header. This reads up to and including the initial * '{' character. Returns 1 if it read a header, 0 at EOF. */ static int read_func_header(char **name, struct params **params, int *paramwid, struct params **rets) { int lastline; char *token; lastline = -1; while (1) { token = read_token(); if (token == nil) return 0; if (streq(token, "func")) { if(lastline != -1) bwritef(output, "\n"); break; } if (lastline != lineno) { if (lastline == lineno-1) bwritef(output, "\n"); else bwritef(output, "\n#line %d \"%s\"\n", lineno, file); lastline = lineno; } bwritef(output, "%s ", token); } *name = read_token_no_eof(); token = read_token(); if (token == nil || !streq(token, "(")) { fatal("%s:%ud: expected \"(\"\n", file, lineno); } *params = read_params(paramwid); token = read_token(); if (token == nil || !streq(token, "(")) *rets = nil; else { *rets = read_params(nil); token = read_token(); } if (token == nil || !streq(token, "{")) { fatal("%s:%ud: expected \"{\"\n", file, lineno); } return 1; } /* Write out parameters. */ static void write_params(struct params *params, int *first) { struct params *p; for (p = params; p != nil; p = p->next) { if (*first) *first = 0; else bwritef(output, ", "); bwritef(output, "%s %s", p->type, p->name); } } /* Write a 6g function header. */ static void write_6g_func_header(char *package, char *name, struct params *params, int paramwid, struct params *rets) { int first, n; bwritef(output, "void\n%s·%s(", package, name); first = 1; write_params(params, &first); /* insert padding to align output struct */ if(rets != nil && paramwid%structround != 0) { n = structround - paramwid%structround; if(n & 1) bwritef(output, ", uint8"); if(n & 2) bwritef(output, ", uint16"); if(n & 4) bwritef(output, ", uint32"); } write_params(rets, &first); bwritef(output, ")\n{\n"); } /* Write a 6g function trailer. */ static void write_6g_func_trailer(struct params *rets) { struct params *p; for (p = rets; p != nil; p = p->next) bwritef(output, "\tFLUSH(&%s);\n", p->name); bwritef(output, "}\n"); } /* Define the gcc function return type if necessary. */ static void define_gcc_return_type(char *package, char *name, struct params *rets) { struct params *p; if (rets == nil || rets->next == nil) return; bwritef(output, "struct %s_%s_ret {\n", package, name); for (p = rets; p != nil; p = p->next) bwritef(output, " %s %s;\n", p->type, p->name); bwritef(output, "};\n"); } /* Write out the gcc function return type. */ static void write_gcc_return_type(char *package, char *name, struct params *rets) { if (rets == nil) bwritef(output, "void"); else if (rets->next == nil) bwritef(output, "%s", rets->type); else bwritef(output, "struct %s_%s_ret", package, name); } /* Write out a gcc function header. */ static void write_gcc_func_header(char *package, char *name, struct params *params, struct params *rets) { int first; struct params *p; define_gcc_return_type(package, name, rets); write_gcc_return_type(package, name, rets); bwritef(output, " %s_%s(", package, name); first = 1; write_params(params, &first); bwritef(output, ") asm (\"%s.%s\");\n", package, name); write_gcc_return_type(package, name, rets); bwritef(output, " %s_%s(", package, name); first = 1; write_params(params, &first); bwritef(output, ")\n{\n"); for (p = rets; p != nil; p = p->next) bwritef(output, " %s %s;\n", p->type, p->name); } /* Write out a gcc function trailer. */ static void write_gcc_func_trailer(char *package, char *name, struct params *rets) { if (rets == nil) ; else if (rets->next == nil) bwritef(output, "return %s;\n", rets->name); else { struct params *p; bwritef(output, " {\n struct %s_%s_ret __ret;\n", package, name); for (p = rets; p != nil; p = p->next) bwritef(output, " __ret.%s = %s;\n", p->name, p->name); bwritef(output, " return __ret;\n }\n"); } bwritef(output, "}\n"); } /* Write out a function header. */ static void write_func_header(char *package, char *name, struct params *params, int paramwid, struct params *rets) { if (gcc) write_gcc_func_header(package, name, params, rets); else write_6g_func_header(package, name, params, paramwid, rets); bwritef(output, "#line %d \"%s\"\n", lineno, file); } /* Write out a function trailer. */ static void write_func_trailer(char *package, char *name, struct params *rets) { if (gcc) write_gcc_func_trailer(package, name, rets); else write_6g_func_trailer(rets); } /* * Read and write the body of the function, ending in an unnested } * (which is read but not written). */ static void copy_body(void) { int nesting = 0; while (1) { int c; c = getchar_no_eof(); if (c == '}' && nesting == 0) return; xputchar(c); switch (c) { default: break; case '{': ++nesting; break; case '}': --nesting; break; case '/': c = getchar_update_lineno(); xputchar(c); if (c == '/') { do { c = getchar_no_eof(); xputchar(c); } while (c != '\n'); } else if (c == '*') { while (1) { c = getchar_no_eof(); xputchar(c); if (c == '*') { do { c = getchar_no_eof(); xputchar(c); } while (c == '*'); if (c == '/') break; } } } break; case '"': case '\'': { int delim = c; do { c = getchar_no_eof(); xputchar(c); if (c == '\\') { c = getchar_no_eof(); xputchar(c); c = '\0'; } } while (c != delim); } break; } } } /* Process the entire file. */ static void process_file(void) { char *package, *name; struct params *params, *rets; int paramwid; package = read_package(); read_preprocessor_lines(); while (read_func_header(&name, ¶ms, ¶mwid, &rets)) { write_func_header(package, name, params, paramwid, rets); copy_body(); write_func_trailer(package, name, rets); xfree(name); free_params(params); free_params(rets); } xfree(package); } void goc2c(char *goc, char *c) { Buf in, out; binit(&in); binit(&out); file = goc; readfile(&in, goc); // TODO: set gcc=1 when using gcc if(!gcc) { if(streq(goarch, "amd64")) { type_table[Uintptr].size = 8; type_table[String].size = 16; type_table[Slice].size = 8+4+4; type_table[Eface].size = 8+8; structround = 8; } else { type_table[Uintptr].size = 4; type_table[String].size = 8; type_table[Slice].size = 16; type_table[Eface].size = 4+4; structround = 4; } } bprintf(&out, "// auto generated by go tool dist\n// goos=%s goarch=%s\n\n", goos, goarch); input = bstr(&in); output = &out; process_file(); writefile(&out, c, 0); }