github.com/prattmic/llgo-embedded@v0.0.0-20150820070356-41cfecea0e1e/third_party/gofrontend/libgo/runtime/goc2c.c (about)

     1  // Copyright 2009 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  // +build ignore
     6  
     7  /*
     8   * Translate a .goc file into a .c file.  A .goc file is a combination
     9   * of a limited form of Go with C.
    10   */
    11  
    12  /*
    13  	package PACKAGENAME
    14  	{# line}
    15  	func NAME([NAME TYPE { , NAME TYPE }]) [(NAME TYPE { , NAME TYPE })] \{
    16  	  C code with proper brace nesting
    17  	\}
    18  */
    19  
    20  /*
    21   * We generate C code which implements the function such that it can
    22   * be called from Go and executes the C code.
    23   */
    24  
    25  #include <assert.h>
    26  #include <ctype.h>
    27  #include <stdarg.h>
    28  #include <stdio.h>
    29  #include <stdlib.h>
    30  #include <string.h>
    31  #include <errno.h>
    32  
    33  /* Package path to use.  */
    34  static const char *pkgpath;
    35  
    36  /* Package prefix to use.  */
    37  static const char *prefix;
    38  
    39  /* File and line number */
    40  static const char *file;
    41  static unsigned int lineno = 1;
    42  
    43  /* List of names and types.  */
    44  struct params {
    45  	struct params *next;
    46  	char *name;
    47  	char *type;
    48  };
    49  
    50  char *argv0;
    51  
    52  static void
    53  sysfatal(char *fmt, ...)
    54  {
    55  	char buf[256];
    56  	va_list arg;
    57  
    58  	va_start(arg, fmt);
    59  	vsnprintf(buf, sizeof buf, fmt, arg);
    60  	va_end(arg);
    61  
    62  	fprintf(stderr, "%s: %s\n", argv0 ? argv0 : "<prog>", buf);
    63  	exit(1);
    64  }
    65  
    66  /* Unexpected EOF.  */
    67  static void
    68  bad_eof(void)
    69  {
    70  	sysfatal("%s:%ud: unexpected EOF\n", file, lineno);
    71  }
    72  
    73  /* Out of memory.  */
    74  static void
    75  bad_mem(void)
    76  {
    77  	sysfatal("%s:%ud: out of memory\n", file, lineno);
    78  }
    79  
    80  /* Allocate memory without fail.  */
    81  static void *
    82  xmalloc(unsigned int size)
    83  {
    84  	void *ret = malloc(size);
    85  	if (ret == NULL)
    86  		bad_mem();
    87  	return ret;
    88  }
    89  
    90  /* Reallocate memory without fail.  */
    91  static void*
    92  xrealloc(void *buf, unsigned int size)
    93  {
    94  	void *ret = realloc(buf, size);
    95  	if (ret == NULL)
    96  		bad_mem();
    97  	return ret;
    98  }
    99  
   100  /* Copy a string into memory without fail.  */
   101  static char *
   102  xstrdup(const char *p)
   103  {
   104  	char *ret = xmalloc(strlen(p) + 1);
   105  	strcpy(ret, p);
   106  	return ret;
   107  }
   108  
   109  /* Free a list of parameters.  */
   110  static void
   111  free_params(struct params *p)
   112  {
   113  	while (p != NULL) {
   114  		struct params *next;
   115  
   116  		next = p->next;
   117  		free(p->name);
   118  		free(p->type);
   119  		free(p);
   120  		p = next;
   121  	}
   122  }
   123  
   124  /* Read a character, tracking lineno.  */
   125  static int
   126  getchar_update_lineno(void)
   127  {
   128  	int c;
   129  
   130  	c = getchar();
   131  	if (c == '\n')
   132  		++lineno;
   133  	return c;
   134  }
   135  
   136  /* Read a character, giving an error on EOF, tracking lineno.  */
   137  static int
   138  getchar_no_eof(void)
   139  {
   140  	int c;
   141  
   142  	c = getchar_update_lineno();
   143  	if (c == EOF)
   144  		bad_eof();
   145  	return c;
   146  }
   147  
   148  /* Read a character, skipping comments.  */
   149  static int
   150  getchar_skipping_comments(void)
   151  {
   152  	int c;
   153  
   154  	while (1) {
   155  		c = getchar_update_lineno();
   156  		if (c != '/')
   157  			return c;
   158  
   159  		c = getchar();
   160  		if (c == '/') {
   161  			do {
   162  				c = getchar_update_lineno();
   163  			} while (c != EOF && c != '\n');
   164  			return c;
   165  		} else if (c == '*') {
   166  			while (1) {
   167  				c = getchar_update_lineno();
   168  				if (c == EOF)
   169  					return EOF;
   170  				if (c == '*') {
   171  					do {
   172  						c = getchar_update_lineno();
   173  					} while (c == '*');
   174  					if (c == '/')
   175  						break;
   176  				}
   177  			}
   178  		} else {
   179  			ungetc(c, stdin);
   180  			return '/';
   181  		}
   182  	}
   183  }
   184  
   185  /*
   186   * Read and return a token.  Tokens are string or character literals
   187   * or else delimited by whitespace or by [(),{}].
   188   * The latter are all returned as single characters.
   189   */
   190  static char *
   191  read_token(void)
   192  {
   193  	int c, q;
   194  	char *buf;
   195  	unsigned int alc, off;
   196  	const char* delims = "(),{}";
   197  
   198  	while (1) {
   199  		c = getchar_skipping_comments();
   200  		if (c == EOF)
   201  			return NULL;
   202  		if (!isspace(c))
   203  			break;
   204  	}
   205  	alc = 16;
   206  	buf = xmalloc(alc + 1);
   207  	off = 0;
   208  	if(c == '"' || c == '\'') {
   209  		q = c;
   210  		buf[off] = c;
   211  		++off;
   212  		while (1) {
   213  			if (off+2 >= alc) { // room for c and maybe next char
   214  				alc *= 2;
   215  				buf = xrealloc(buf, alc + 1);
   216  			}
   217  			c = getchar_no_eof();
   218  			buf[off] = c;
   219  			++off;
   220  			if(c == q)
   221  				break;
   222  			if(c == '\\') {
   223  				buf[off] = getchar_no_eof();
   224  				++off;
   225  			}
   226  		}
   227  	} else if (strchr(delims, c) != NULL) {
   228  		buf[off] = c;
   229  		++off;
   230  	} else {
   231  		while (1) {
   232  			if (off >= alc) {
   233  				alc *= 2;
   234  				buf = xrealloc(buf, alc + 1);
   235  			}
   236  			buf[off] = c;
   237  			++off;
   238  			c = getchar_skipping_comments();
   239  			if (c == EOF)
   240  				break;
   241  			if (isspace(c) || strchr(delims, c) != NULL) {
   242  				if (c == '\n')
   243  					lineno--;
   244  				ungetc(c, stdin);
   245  				break;
   246  			}
   247  		}
   248  	}
   249  	buf[off] = '\0';
   250  	return buf;
   251  }
   252  
   253  /* Read a token, giving an error on EOF.  */
   254  static char *
   255  read_token_no_eof(void)
   256  {
   257  	char *token = read_token();
   258  	if (token == NULL)
   259  		bad_eof();
   260  	return token;
   261  }
   262  
   263  /* Read the package clause, and return the package name.  */
   264  static char *
   265  read_package(void)
   266  {
   267  	char *token;
   268  
   269  	token = read_token_no_eof();
   270  	if (token == NULL)
   271  		sysfatal("%s:%ud: no token\n", file, lineno);
   272  	if (strcmp(token, "package") != 0) {
   273  		sysfatal("%s:%ud: expected \"package\", got \"%s\"\n",
   274  			file, lineno, token);
   275  	}
   276  	return read_token_no_eof();
   277  }
   278  
   279  /* Read and copy preprocessor lines.  */
   280  static void
   281  read_preprocessor_lines(void)
   282  {
   283  	while (1) {
   284  		int c;
   285  
   286  		do {
   287  			c = getchar_skipping_comments();
   288  		} while (isspace(c));
   289  		if (c != '#') {
   290  			ungetc(c, stdin);
   291  			break;
   292  		}
   293  		putchar(c);
   294  		do {
   295  			c = getchar_update_lineno();
   296  			putchar(c);
   297  		} while (c != '\n');
   298  	}
   299  }
   300  
   301  /*
   302   * Read a type in Go syntax and return a type in C syntax.  We only
   303   * permit basic types and pointers.
   304   */
   305  static char *
   306  read_type(void)
   307  {
   308  	char *p, *op, *q;
   309  	int pointer_count;
   310  	unsigned int len;
   311  
   312  	p = read_token_no_eof();
   313  	if (*p != '*') {
   314  		/* Convert the Go type "int" to the C type "intgo",
   315  		   and similarly for "uint".  */
   316  		if (strcmp(p, "int") == 0)
   317  			return xstrdup("intgo");
   318  		else if (strcmp(p, "uint") == 0)
   319  			return xstrdup("uintgo");
   320  		return p;
   321  	}
   322  	op = p;
   323  	pointer_count = 0;
   324  	while (*p == '*') {
   325  		++pointer_count;
   326  		++p;
   327  	}
   328  
   329  	/* Convert the Go type "int" to the C type "intgo", and
   330  	   similarly for "uint".  */
   331  	if (strcmp(p, "int") == 0)
   332  	  p = (char *) "intgo";
   333  	else if (strcmp(p, "uint") == 0)
   334  	  p = (char *) "uintgo";
   335  
   336  	len = strlen(p);
   337  	q = xmalloc(len + pointer_count + 1);
   338  	memcpy(q, p, len);
   339  	while (pointer_count > 0) {
   340  		q[len] = '*';
   341  		++len;
   342  		--pointer_count;
   343  	}
   344  	q[len] = '\0';
   345  	free(op);
   346  	return q;
   347  }
   348  
   349  /*
   350   * Read a list of parameters.  Each parameter is a name and a type.
   351   * The list ends with a ')'.  We have already read the '('.
   352   */
   353  static struct params *
   354  read_params()
   355  {
   356  	char *token;
   357  	struct params *ret, **pp, *p;
   358  
   359  	ret = NULL;
   360  	pp = &ret;
   361  	token = read_token_no_eof();
   362  	if (strcmp(token, ")") != 0) {
   363  		while (1) {
   364  			p = xmalloc(sizeof(struct params));
   365  			p->name = token;
   366  			p->type = read_type();
   367  			p->next = NULL;
   368  			*pp = p;
   369  			pp = &p->next;
   370  
   371  			token = read_token_no_eof();
   372  			if (strcmp(token, ",") != 0)
   373  				break;
   374  			token = read_token_no_eof();
   375  		}
   376  	}
   377  	if (strcmp(token, ")") != 0) {
   378  		sysfatal("%s:%ud: expected '('\n",
   379  			file, lineno);
   380  	}
   381  	return ret;
   382  }
   383  
   384  /*
   385   * Read a function header.  This reads up to and including the initial
   386   * '{' character.  Returns 1 if it read a header, 0 at EOF.
   387   */
   388  static int
   389  read_func_header(char **name, struct params **params, struct params **rets)
   390  {
   391  	int lastline;
   392  	char *token;
   393  
   394  	lastline = -1;
   395  	while (1) {
   396  		token = read_token();
   397  		if (token == NULL)
   398  			return 0;
   399  		if (strcmp(token, "func") == 0) {
   400  			if(lastline != -1)
   401  				printf("\n");
   402  			break;
   403  		}
   404  		if (lastline != lineno) {
   405  			if (lastline == lineno-1)
   406  				printf("\n");
   407  			else
   408  				printf("\n#line %d \"%s\"\n", lineno, file);
   409  			lastline = lineno;
   410  		}
   411  		printf("%s ", token);
   412  	}
   413  
   414  	*name = read_token_no_eof();
   415  
   416  	token = read_token();
   417  	if (token == NULL || strcmp(token, "(") != 0) {
   418  		sysfatal("%s:%ud: expected \"(\"\n",
   419  			file, lineno);
   420  	}
   421  	*params = read_params();
   422  
   423  	token = read_token();
   424  	if (token == NULL || strcmp(token, "(") != 0)
   425  		*rets = NULL;
   426  	else {
   427  		*rets = read_params();
   428  		token = read_token();
   429  	}
   430  	if (token == NULL || strcmp(token, "{") != 0) {
   431  		sysfatal("%s:%ud: expected \"{\"\n",
   432  			file, lineno);
   433  	}
   434  	return 1;
   435  }
   436  
   437  /* Write out parameters.  */
   438  static void
   439  write_params(struct params *params, int *first)
   440  {
   441  	struct params *p;
   442  
   443  	for (p = params; p != NULL; p = p->next) {
   444  		if (*first)
   445  			*first = 0;
   446  		else
   447  			printf(", ");
   448  		printf("%s %s", p->type, p->name);
   449  	}
   450  }
   451  
   452  /* Define the gcc function return type if necessary.  */
   453  static void
   454  define_gcc_return_type(char *package, char *name, struct params *rets)
   455  {
   456  	struct params *p;
   457  
   458  	if (rets == NULL || rets->next == NULL)
   459  		return;
   460  	printf("struct %s_%s_ret {\n", package, name);
   461  	for (p = rets; p != NULL; p = p->next)
   462  		printf("  %s %s;\n", p->type, p->name);
   463  	printf("};\n");
   464  }
   465  
   466  /* Write out the gcc function return type.  */
   467  static void
   468  write_gcc_return_type(char *package, char *name, struct params *rets)
   469  {
   470  	if (rets == NULL)
   471  		printf("void");
   472  	else if (rets->next == NULL)
   473  		printf("%s", rets->type);
   474  	else
   475  		printf("struct %s_%s_ret", package, name);
   476  }
   477  
   478  /* Write out a gcc function header.  */
   479  static void
   480  write_gcc_func_header(char *package, char *name, struct params *params,
   481  		      struct params *rets)
   482  {
   483  	int first;
   484  	struct params *p;
   485  
   486  	define_gcc_return_type(package, name, rets);
   487  	write_gcc_return_type(package, name, rets);
   488  	printf(" %s_%s(", package, name);
   489  	first = 1;
   490  	write_params(params, &first);
   491  	printf(") __asm__ (GOSYM_PREFIX \"");
   492  	if (pkgpath != NULL)
   493  	  printf("%s", pkgpath);
   494  	else if (prefix != NULL)
   495  	  printf("%s.%s", prefix, package);
   496  	else
   497  	  printf("%s", package);
   498  	printf(".%s\");\n", name);
   499  	write_gcc_return_type(package, name, rets);
   500  	printf(" %s_%s(", package, name);
   501  	first = 1;
   502  	write_params(params, &first);
   503  	printf(")\n{\n");
   504  	for (p = rets; p != NULL; p = p->next)
   505  		printf("  %s %s;\n", p->type, p->name);
   506  }
   507  
   508  /* Write out a gcc function trailer.  */
   509  static void
   510  write_gcc_func_trailer(char *package, char *name, struct params *rets)
   511  {
   512  	if (rets == NULL)
   513  		;
   514  	else if (rets->next == NULL)
   515  		printf("return %s;\n", rets->name);
   516  	else {
   517  		struct params *p;
   518  
   519  		printf("  {\n    struct %s_%s_ret __ret;\n", package, name);
   520  		for (p = rets; p != NULL; p = p->next)
   521  			printf("    __ret.%s = %s;\n", p->name, p->name);
   522  		printf("    return __ret;\n  }\n");
   523  	}
   524  	printf("}\n");
   525  }
   526  
   527  /* Write out a function header.  */
   528  static void
   529  write_func_header(char *package, char *name, struct params *params, 
   530  		  struct params *rets)
   531  {
   532  	write_gcc_func_header(package, name, params, rets);
   533  	printf("#line %d \"%s\"\n", lineno, file);
   534  }
   535  
   536  /* Write out a function trailer.  */
   537  static void
   538  write_func_trailer(char *package, char *name,
   539  		   struct params *rets)
   540  {
   541  	write_gcc_func_trailer(package, name, rets);
   542  }
   543  
   544  /*
   545   * Read and write the body of the function, ending in an unnested }
   546   * (which is read but not written).
   547   */
   548  static void
   549  copy_body(void)
   550  {
   551  	int nesting = 0;
   552  	while (1) {
   553  		int c;
   554  
   555  		c = getchar_no_eof();
   556  		if (c == '}' && nesting == 0)
   557  			return;
   558  		putchar(c);
   559  		switch (c) {
   560  		default:
   561  			break;
   562  		case '{':
   563  			++nesting;
   564  			break;
   565  		case '}':
   566  			--nesting;
   567  			break;
   568  		case '/':
   569  			c = getchar_update_lineno();
   570  			putchar(c);
   571  			if (c == '/') {
   572  				do {
   573  					c = getchar_no_eof();
   574  					putchar(c);
   575  				} while (c != '\n');
   576  			} else if (c == '*') {
   577  				while (1) {
   578  					c = getchar_no_eof();
   579  					putchar(c);
   580  					if (c == '*') {
   581  						do {
   582  							c = getchar_no_eof();
   583  							putchar(c);
   584  						} while (c == '*');
   585  						if (c == '/')
   586  							break;
   587  					}
   588  				}
   589  			}
   590  			break;
   591  		case '"':
   592  		case '\'':
   593  			{
   594  				int delim = c;
   595  				do {
   596  					c = getchar_no_eof();
   597  					putchar(c);
   598  					if (c == '\\') {
   599  						c = getchar_no_eof();
   600  						putchar(c);
   601  						c = '\0';
   602  					}
   603  				} while (c != delim);
   604  			}
   605  			break;
   606  		}
   607  	}
   608  }
   609  
   610  /* Process the entire file.  */
   611  static void
   612  process_file(void)
   613  {
   614  	char *package, *name;
   615  	struct params *params, *rets;
   616  
   617  	package = read_package();
   618  	read_preprocessor_lines();
   619  	while (read_func_header(&name, &params, &rets)) {
   620  		char *p;
   621  		char *pkg;
   622  		char *nm;
   623  
   624  		p = strchr(name, '.');
   625  		if (p == NULL) {
   626  			pkg = package;
   627  			nm = name;
   628  		} else {
   629  			pkg = name;
   630  			nm = p + 1;
   631  			*p = '\0';
   632  		}
   633  		write_func_header(pkg, nm, params, rets);
   634  		copy_body();
   635  		write_func_trailer(pkg, nm, rets);
   636  		free(name);
   637  		free_params(params);
   638  		free_params(rets);
   639  	}
   640  	free(package);
   641  }
   642  
   643  static void
   644  usage(void)
   645  {
   646  	sysfatal("Usage: goc2c [--go-pkgpath PKGPATH] [--go-prefix PREFIX] [file]\n");
   647  }
   648  
   649  int
   650  main(int argc, char **argv)
   651  {
   652  	char *goarch;
   653  
   654  	argv0 = argv[0];
   655  	while(argc > 1 && argv[1][0] == '-') {
   656  		if(strcmp(argv[1], "-") == 0)
   657  			break;
   658  		if (strcmp(argv[1], "--go-pkgpath") == 0 && argc > 2) {
   659  			pkgpath = argv[2];
   660  			argc--;
   661  			argv++;
   662  		} else if (strcmp(argv[1], "--go-prefix") == 0 && argc > 2) {
   663  			prefix = argv[2];
   664  			argc--;
   665  			argv++;
   666  		} else
   667  			usage();
   668  		argc--;
   669  		argv++;
   670  	}
   671  
   672  	if(argc <= 1 || strcmp(argv[1], "-") == 0) {
   673  		file = "<stdin>";
   674  		process_file();
   675  		exit(0);
   676  	}
   677  
   678  	if(argc > 2)
   679  		usage();
   680  
   681  	file = argv[1];
   682  	if(freopen(file, "r", stdin) == 0) {
   683  		sysfatal("open %s: %r\n", file);
   684  	}
   685  
   686  	printf("// AUTO-GENERATED by autogen.sh; DO NOT EDIT\n\n");
   687  	process_file();
   688  	exit(0);
   689  }