Importing Upstream version 4.8.2
[platform/upstream/gcc48.git] / libgo / runtime / goc2c.c
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                 write_func_header(package, name, params, rets);
621                 copy_body();
622                 write_func_trailer(package, name, rets);
623                 free(name);
624                 free_params(params);
625                 free_params(rets);
626         }
627         free(package);
628 }
629
630 static void
631 usage(void)
632 {
633         sysfatal("Usage: goc2c [--go-pkgpath PKGPATH] [--go-prefix PREFIX] [file]\n");
634 }
635
636 int
637 main(int argc, char **argv)
638 {
639         char *goarch;
640
641         argv0 = argv[0];
642         while(argc > 1 && argv[1][0] == '-') {
643                 if(strcmp(argv[1], "-") == 0)
644                         break;
645                 if (strcmp(argv[1], "--go-pkgpath") == 0 && argc > 2) {
646                         pkgpath = argv[2];
647                         argc--;
648                         argv++;
649                 } else if (strcmp(argv[1], "--go-prefix") == 0 && argc > 2) {
650                         prefix = argv[2];
651                         argc--;
652                         argv++;
653                 } else
654                         usage();
655                 argc--;
656                 argv++;
657         }
658
659         if(argc <= 1 || strcmp(argv[1], "-") == 0) {
660                 file = "<stdin>";
661                 process_file();
662                 exit(0);
663         }
664
665         if(argc > 2)
666                 usage();
667
668         file = argv[1];
669         if(freopen(file, "r", stdin) == 0) {
670                 sysfatal("open %s: %r\n", file);
671         }
672
673         printf("// AUTO-GENERATED by autogen.sh; DO NOT EDIT\n\n");
674         process_file();
675         exit(0);
676 }