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, ¶ms, &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 }