runtime: fix context used by getTraceback
[gcc.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 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 }