blob: 68281c36bca4ad453400c6ac092fb60c581a5898 [file] [log] [blame]
Peter Collingbourne594c10d2014-11-27 00:12:26 +00001// 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. */
34static const char *pkgpath;
35
36/* Package prefix to use. */
37static const char *prefix;
38
39/* File and line number */
40static const char *file;
41static unsigned int lineno = 1;
42
43/* List of names and types. */
44struct params {
45 struct params *next;
46 char *name;
47 char *type;
48};
49
50char *argv0;
51
52static void
53sysfatal(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. */
67static void
68bad_eof(void)
69{
70 sysfatal("%s:%ud: unexpected EOF\n", file, lineno);
71}
72
73/* Out of memory. */
74static void
75bad_mem(void)
76{
77 sysfatal("%s:%ud: out of memory\n", file, lineno);
78}
79
80/* Allocate memory without fail. */
81static void *
82xmalloc(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. */
91static void*
92xrealloc(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. */
101static char *
102xstrdup(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. */
110static void
111free_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. */
125static int
126getchar_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. */
137static int
138getchar_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. */
149static int
150getchar_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 */
190static char *
191read_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. */
254static char *
255read_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. */
264static char *
265read_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. */
280static void
281read_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 */
305static char *
306read_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 */
353static struct params *
354read_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 */
388static int
389read_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. */
438static void
439write_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. */
453static void
454define_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. */
467static void
468write_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. */
479static void
480write_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. */
509static void
510write_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. */
528static void
529write_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. */
537static void
538write_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 */
548static void
549copy_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. */
611static void
612process_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
643static void
644usage(void)
645{
646 sysfatal("Usage: goc2c [--go-pkgpath PKGPATH] [--go-prefix PREFIX] [file]\n");
647}
648
649int
650main(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}