1 %{ 2 3 /*- 4 * Copyright (c) 1988, 1993 5 * The Regents of the University of California. All rights reserved. 6 * 7 * %sccs.include.redist.c% 8 */ 9 10 #ifndef lint 11 static char sccsid[] = "@(#)mkmake.y 8.1 (Berkeley) 06/06/93"; 12 #endif /* not lint */ 13 14 typedef struct string { 15 int 16 hashval, 17 length; 18 char 19 *string; 20 struct string 21 *next; 22 } string_t; 23 24 /* 25 * The deal with these is that they exist on various lists. 26 * 27 * First off, they are on a temporary list during the time they 28 * are in the active focus of the parser. 29 * 30 * Secondly, they live on one of three queues: 31 * 1. Variables 32 * 2. Targets 33 * 3. Actions 34 * (and, we restrict any given one to live on one and only one such list) 35 * 36 * Also, they may live on the list of values for someone else's variable, 37 * or as someone's dependancy. 38 */ 39 40 typedef struct same { 41 string_t 42 *string; /* My name */ 43 struct same 44 *nexttoken, /* Next pointer */ 45 *lasttoken, /* Back pointer */ 46 *depend_list, /* If target, dependancies */ 47 *action_list, /* If target, actions */ 48 *value_list, /* If variable, value list */ 49 *shell_item; /* If a shell variable, current value */ 50 } same_t; 51 52 %} 53 54 %union { 55 string_t *string; 56 same_t *same; 57 int intval; 58 } 59 60 %start makefile 61 %token <string> TOKEN QUOTED_STRING 62 %token <intval> FOR IN DO DONE 63 %token <intval> MACRO_CHAR NL WHITE_SPACE 64 %token <intval> ':' '=' '$' '{' '}' ';' '-' '@' '(' ')' ' ' '\t' 65 %type <same> target target1 assignment assign1 actions action 66 %type <same> command_list list list_element 67 %type <same> for_statement maybe_at_minus tokens token 68 %type <same> maybe_white_space 69 %type <intval> white_space macro_char 70 %% 71 72 makefile : lines; 73 74 lines : line 75 | lines line 76 ; 77 78 line : NL 79 | assignment 80 | target_action 81 ; 82 83 assignment : assign1 tokens NL 84 { 85 assign($1, $2); 86 } 87 | assign1 NL 88 { 89 assign($1, same_copy(null)); 90 } 91 ; 92 93 assign1: token maybe_white_space '=' maybe_white_space 94 ; 95 96 target_action: target actions 97 { 98 add_targets_actions($1, $2); 99 } 100 | target 101 { 102 add_targets_actions($1, 0); 103 } 104 ; 105 106 target : target1 tokens NL 107 { 108 $$ = add_depends($1, $2); 109 } 110 | target1 NL 111 { 112 $$ = add_depends($1, same_copy(null)); 113 } 114 ; 115 116 target1: tokens maybe_white_space ':' maybe_white_space 117 { 118 $$ = ws_merge($1); 119 } 120 ; 121 122 actions: action 123 | actions action 124 { 125 $$ = same_cat(same_cat($1, same_copy(newline)), $2); 126 } 127 ; 128 129 action: white_space command_list NL 130 { 131 $$ = $2; 132 } 133 | white_space for_statement do command_list semi_colon done NL 134 { 135 $$ = do_command($2, $4); 136 } 137 ; 138 139 for_statement: maybe_at_minus FOR white_space token 140 in tokens semi_colon 141 { 142 $$ = for_statement($1, $4, ws_merge(expand_variables($6, 0))); 143 } 144 ; 145 146 in: white_space IN white_space 147 do: white_space DO white_space 148 ; 149 150 done: white_space DONE 151 ; 152 153 semi_colon: ';' 154 ; 155 156 command_list: list 157 | '(' list maybe_white_space ')' 158 { 159 $$ = same_cat($2, same_copy(cwd_line)); 160 } 161 ; 162 163 list: token 164 | list list_element 165 { 166 $$ = same_cat($1, $2); 167 } 168 | list white_space list_element 169 { 170 $$ = same_cat($1, same_cat(same_copy(blank), $3)); 171 } 172 ; 173 174 list_element: token 175 | semi_colon 176 { 177 $$ = same_copy(newline); 178 } 179 ; 180 181 maybe_at_minus: /* empty */ 182 { 183 $$ = same_copy(null); 184 } 185 | '@' 186 { 187 char buffer[2]; 188 189 buffer[0] = $1; 190 buffer[1] = 0; 191 $$ = same_item(string_lookup(buffer)); 192 } 193 | '-' 194 { 195 char buffer[2]; 196 197 buffer[0] = $1; 198 buffer[1] = 0; 199 $$ = same_item(string_lookup(buffer)); 200 } 201 ; 202 203 tokens : token 204 | tokens maybe_white_space token 205 { 206 $$ = same_cat($1, same_cat($2, $3)); 207 } 208 ; 209 210 token: TOKEN 211 { 212 $$ = same_item($1); 213 } 214 | QUOTED_STRING 215 { 216 $$ = same_item($1); 217 } 218 | '$' macro_char 219 { 220 char buffer[3]; 221 222 buffer[0] = '$'; 223 buffer[1] = $2; 224 buffer[2] = 0; 225 226 $$ = same_item(string_lookup(buffer)); 227 } 228 | '$' '$' TOKEN 229 { 230 $$ = shell_variable(same_item($3)); 231 } 232 | MACRO_CHAR 233 { 234 $$ = same_char($1); 235 } 236 | '$' '{' TOKEN '}' 237 { 238 $$ = variable(same_item($3)); 239 } 240 | '$' '(' TOKEN ')' 241 { 242 $$ = variable(same_item($3)); 243 } 244 | '$' TOKEN 245 { 246 $$ = variable(same_item($2)); 247 } 248 | '-' 249 { 250 $$ = same_char('-'); 251 } 252 | '@' 253 { 254 $$ = same_char('@'); 255 } 256 ; 257 258 macro_char: MACRO_CHAR 259 | '@' 260 ; 261 262 maybe_white_space: 263 { 264 $$ = same_copy(null); 265 } 266 | white_space 267 { 268 $$ = same_char($1); 269 } 270 ; 271 272 white_space : WHITE_SPACE 273 | white_space WHITE_SPACE 274 ; 275 %% 276 #include <stdio.h> 277 #include <ctype.h> 278 279 static int last_char, last_saved = 0; 280 static int column = 0, lineno = 1; 281 282 283 static string_t 284 *strings = 0; 285 286 static same_t 287 *shell_variables = 0, 288 *shell_special = 0, 289 *variables = 0, 290 *targets = 0, 291 *actions = 0; 292 293 static same_t 294 *null, 295 *blank, 296 *cwd_line, 297 *newline; 298 299 extern char *malloc(); 300 301 static unsigned int 302 clock = -1; 303 304 struct { 305 same_t *first; 306 int next; 307 } visit_stack[20]; /* 20 maximum */ 308 309 #define visit(what,via) \ 310 (visit_stack[++clock].next = 0, visit_stack[clock].first = via = what) 311 #define visited(via) (visitcheck(via) || ((via) == 0) \ 312 || (visit_stack[clock].next && (via == visit_stack[clock].first))) 313 #define visit_next(via) (visit_stack[clock].next = 1, (via) = (via)->nexttoken) 314 #define visit_end() (clock--) 315 316 yyerror(s) 317 char *s; 318 { 319 fprintf(stderr, "line %d, character %d: %s\n", lineno, column, s); 320 do_dump(); 321 } 322 323 int 324 visitcheck(same) 325 same_t *same; 326 { 327 if (same->string == 0) { 328 yyerror("BUG - freed 'same' in use..."); 329 exit(1); 330 } 331 return 0; 332 } 333 334 int 335 string_hashof(string, length) 336 char *string; 337 int length; 338 { 339 register int i = 0; 340 341 while (length--) { 342 i = (i<<3) + *string ^ ((i>>28)&0x7); 343 } 344 return i; 345 } 346 347 int 348 string_same(s1, s2) 349 string_t 350 *s1, *s2; 351 { 352 if ((s1->hashval == s2->hashval) && (s1->length == s2->length) 353 && (memcmp(s1->string, s2->string, s1->length) == 0)) { 354 return 1; 355 } else { 356 return 0; 357 } 358 } 359 360 string_t * 361 string_lookup(string) 362 char *string; 363 { 364 string_t ours; 365 string_t *ptr; 366 367 ours.length = strlen(string); 368 ours.hashval = string_hashof(string, ours.length); 369 ours.string = string; 370 371 for (ptr = strings; ptr; ptr = ptr->next) { 372 if (string_same(&ours, ptr)) { 373 return ptr; 374 } 375 } 376 if ((ptr = (string_t *)malloc(sizeof *ptr)) == 0) { 377 fprintf(stderr, "No space to add string *%s*!\n", string); 378 exit(1); 379 } 380 ptr->hashval = ours.hashval; 381 ptr->length = ours.length; 382 if ((ptr->string = malloc(ours.length+1)) == 0) { 383 fprintf(stderr, "No space to add literal *%s*!\n", string); 384 exit(1); 385 } 386 memcpy(ptr->string, string, ours.length+1); 387 ptr->next = strings; 388 strings = ptr; 389 return ptr; 390 } 391 392 #define same_singleton(s) ((s)->nexttoken == (s)) 393 394 same_t * 395 same_search(list, token) 396 same_t 397 *list, 398 *token; 399 { 400 same_t *ptr; 401 402 ptr = list; 403 for (visit(list, ptr); !visited(ptr); visit_next(ptr)) { 404 string_t *string; 405 406 string = ptr->string; 407 if (string_same(string, token->string)) { 408 visit_end(); 409 return ptr; 410 } 411 } 412 visit_end(); 413 return 0; 414 } 415 416 same_t * 417 same_cat(list, tokens) 418 same_t 419 *list, 420 *tokens; 421 { 422 same_t *last; 423 424 if (tokens == 0) { 425 return list; 426 } 427 if (list) { 428 last = tokens->lasttoken; 429 tokens->lasttoken = list->lasttoken; 430 list->lasttoken = last; 431 tokens->lasttoken->nexttoken = tokens; 432 last->nexttoken = list; 433 return list; 434 } else { 435 return tokens; 436 } 437 } 438 439 same_t * 440 same_item(string) 441 string_t *string; 442 { 443 same_t *ptr; 444 445 if ((ptr = (same_t *)malloc(sizeof *ptr)) == 0) { 446 fprintf(stderr, "No more space for tokens!\n"); 447 exit(1); 448 } 449 memset((char *)ptr, 0, sizeof *ptr); 450 ptr->nexttoken = ptr->lasttoken = ptr; 451 ptr->string = string; 452 return ptr; 453 } 454 455 same_t * 456 same_copy(same) 457 same_t *same; 458 { 459 same_t *head, *copy; 460 461 head = 0; 462 for (visit(same, copy); !visited(copy); visit_next(copy)) { 463 same_t *ptr; 464 465 ptr = same_item(copy->string); 466 head = same_cat(head, ptr); 467 } 468 visit_end(); 469 return head; 470 } 471 472 473 same_t * 474 same_merge(t1, t2) 475 same_t 476 *t1, 477 *t2; 478 { 479 if (same_singleton(t1) && same_singleton(t2)) { 480 int length = strlen(t1->string->string)+strlen(t2->string->string); 481 char *buffer = malloc(length+1); 482 same_t *value; 483 484 if (buffer == 0) { 485 yyerror("No space to merge strings in same_merge!"); 486 exit(1); 487 } 488 strcpy(buffer, t1->string->string); 489 strcat(buffer, t2->string->string); 490 value = same_item(string_lookup(buffer)); 491 free(buffer); 492 return value; 493 } else { 494 yyerror("Internal error - same_merge with non-singletons"); 495 exit(1); 496 } 497 } 498 499 500 void 501 same_free(list) 502 same_t *list; 503 { 504 same_t *token, *ptr; 505 506 if (list == 0) { 507 return; 508 } 509 510 token = list; 511 do { 512 ptr = token->nexttoken; 513 token->string = 0; 514 (void) free((char *)token); 515 token = ptr; 516 } while (token != list); 517 } 518 519 same_t * 520 same_unlink(token) 521 same_t 522 *token; 523 { 524 same_t *tmp; 525 526 if (token == 0) { 527 return 0; 528 } 529 if ((tmp = token->nexttoken) == token) { 530 tmp = 0; 531 } 532 token->lasttoken->nexttoken = token->nexttoken; 533 token->nexttoken->lasttoken = token->lasttoken; 534 token->nexttoken = token->lasttoken = token; 535 return tmp; 536 } 537 538 void 539 same_replace(old, new) 540 same_t 541 *old, 542 *new; 543 { 544 new->lasttoken->nexttoken = old->nexttoken; 545 old->nexttoken->lasttoken = new->lasttoken; 546 new->lasttoken = old->lasttoken; 547 /* rather than 548 * old->lasttoken->nexttoken = new 549 * we update in place (for the case where there isn't anything else) 550 */ 551 *old = *new; 552 } 553 554 555 same_t * 556 same_char(ch) 557 char ch; 558 { 559 char buffer[2]; 560 561 buffer[0] = ch; 562 buffer[1] = 0; 563 564 return same_item(string_lookup(buffer)); 565 } 566 567 568 void 569 add_target(target, actions) 570 same_t 571 *target, 572 *actions; 573 { 574 same_t *ptr; 575 576 if ((ptr = same_search(targets, target)) == 0) { 577 targets = same_cat(targets, target); 578 ptr = target; 579 } else { 580 ptr->depend_list = same_cat(ptr->depend_list, target->depend_list); 581 } 582 if (actions) { 583 if (ptr->action_list) { 584 same_free(ptr->action_list); 585 } 586 ptr->action_list = same_copy(actions); 587 } 588 } 589 590 591 same_t * 592 add_targets_actions(target, actions) 593 same_t 594 *target, 595 *actions; 596 { 597 same_t *ptr; 598 599 if (target == 0) { 600 return 0; 601 } 602 do { 603 ptr = same_unlink(target); 604 add_target(target, actions); 605 target = ptr; 606 } while (target); 607 608 same_free(actions); 609 return 0; 610 } 611 612 same_t * 613 add_depends(target, depends) 614 same_t 615 *target, 616 *depends; 617 { 618 same_t *original = target; 619 620 depends = same_cat(depends, same_copy(blank)); /* Separator */ 621 622 for (visit(original, target); !visited(target); visit_next(target)) { 623 target->depend_list = same_cat(target->depend_list, same_copy(depends)); 624 } 625 visit_end(); 626 same_free(depends); 627 628 return original; 629 } 630 631 632 /* 633 * We know that variable is a singleton 634 */ 635 636 void 637 assign(variable, value) 638 same_t 639 *variable, 640 *value; 641 { 642 same_t *ptr; 643 644 if ((ptr = same_search(variables, variable)) != 0) { 645 same_free(ptr->value_list); 646 variables = same_unlink(ptr); 647 same_free(ptr); 648 } 649 variable->value_list = value; 650 variables = same_cat(variables, variable); 651 } 652 653 same_t * 654 value_of(variable) 655 same_t *variable; 656 { 657 same_t *ptr = same_search(variables, variable); 658 659 if (ptr == 0) { 660 return same_copy(null); 661 } else { 662 return same_copy(ptr->value_list); 663 } 664 } 665 666 667 same_t * 668 expand_variables(token, free) 669 same_t *token; 670 int free; 671 { 672 same_t *head = 0; 673 674 if (!free) { 675 token = same_copy(token); /* Get our private copy */ 676 } 677 678 while (token) { 679 char *string = token->string->string; 680 same_t *tmp = same_unlink(token); 681 682 if ((string[0] == '$') && (string[1] == '{')) { /* Expand time */ 683 int len = strlen(string); 684 685 string[len-1] = 0; 686 head = same_cat(head, expand_variables( 687 value_of(same_item(string_lookup(string+2))), 1)); 688 string[len-1] = '}'; 689 } else { 690 head = same_cat(head, token); 691 } 692 token = tmp; 693 } 694 return head; 695 } 696 697 698 same_t * 699 ws_merge(list) 700 same_t *list; 701 { 702 same_t *newlist = 0, *item; 703 int what = 0; 704 705 while (list) { 706 switch (what) { 707 case 0: 708 if (isspace(list->string->string[0])) { 709 ; 710 } else { 711 item = same_item(list->string); 712 what = 1; 713 } 714 break; 715 case 1: 716 if (isspace(list->string->string[0])) { 717 newlist = same_cat(newlist, item); 718 item = 0; 719 what = 0; 720 } else { 721 item = same_merge(item, same_item(list->string)); 722 what = 1; 723 } 724 break; 725 } 726 list = same_unlink(list); 727 } 728 return same_cat(newlist, item); 729 } 730 731 732 same_t * 733 variable(var_name) 734 same_t *var_name; 735 { 736 int length = strlen(var_name->string->string); 737 same_t *resolved; 738 char *newname; 739 740 if ((newname = malloc(length+1+3)) == 0) { 741 fprintf("Out of space for a variable name.\n"); 742 exit(1); 743 } 744 newname[0] = '$'; 745 newname[1] = '{'; 746 strcpy(newname+2, var_name->string->string); 747 strcat(newname, "}"); 748 resolved = same_item(string_lookup(newname)); 749 free(newname); 750 751 return resolved; 752 } 753 754 755 same_t * 756 shell_variable(var_name) 757 same_t *var_name; 758 { 759 int length = strlen(var_name->string->string); 760 same_t *resolved; 761 char *newname; 762 763 if ((newname = malloc(length+1+2)) == 0) { 764 fprintf("Out of space for a variable name.\n"); 765 exit(1); 766 } 767 newname[0] = '$'; 768 newname[1] = '$'; 769 strcpy(newname+2, var_name->string->string); 770 resolved = same_item(string_lookup(newname)); 771 free(newname); 772 773 return resolved; 774 } 775 776 same_t * 777 for_statement(special, variable, list) 778 same_t 779 *special, 780 *variable, 781 *list; 782 { 783 variable->shell_item = special; 784 variable->value_list = list; 785 return variable; 786 } 787 788 same_t * 789 do_command(forlist, commands) 790 same_t 791 *forlist, 792 *commands; 793 { 794 same_t 795 *special, 796 *command_list = 0, 797 *new_commands, 798 *tmp, 799 *shell_item, 800 *value_list = forlist->value_list; 801 char 802 *tmpstr, 803 *variable_name = forlist->string->string; 804 805 special = forlist->shell_item; 806 if (same_unlink(forlist->shell_item) != 0) { 807 yyerror("Unexpected second item in special part of do_command"); 808 exit(1); 809 } 810 811 while ((shell_item = value_list) != 0) { 812 value_list = same_unlink(shell_item); 813 /* Visit each item in commands. For each shell variable which 814 * matches ours, replace it with ours. 815 */ 816 new_commands = same_copy(commands); 817 for (visit(new_commands, tmp); !visited(tmp); visit_next(tmp)) { 818 tmpstr = tmp->string->string; 819 if ((tmpstr[0] == '$') && (tmpstr[1] == '$')) { 820 if (strcmp(tmpstr+2, variable_name) == 0) { 821 same_replace(tmp, same_copy(shell_item)); 822 } 823 } 824 } 825 visit_end(); 826 command_list = same_cat(command_list, new_commands); 827 } 828 return same_cat(command_list, same_copy(newline)); 829 } 830 831 832 int 833 Getchar() 834 { 835 if (last_saved) { 836 last_saved = 0; 837 return last_char; 838 } else { 839 int c; 840 c = getchar(); 841 switch (c) { 842 case '\n': 843 lineno++; 844 column = 0; 845 break; 846 default: 847 column++; 848 } 849 return c; 850 } 851 } 852 853 854 int 855 token_type(string) 856 char *string; 857 { 858 switch (string[0]) { 859 case 'f': 860 if (strcmp(string, "for") == 0) { 861 return FOR; 862 } 863 break; 864 case 'd': 865 if (string[1] == 'o') { 866 if (strcmp(string, "do") == 0) { 867 return DO; 868 } else if (strcmp(string, "done") == 0) { 869 return DONE; 870 } 871 } 872 break; 873 case 'i': 874 if (strcmp(string, "in") == 0) { 875 return IN; 876 } 877 break; 878 default: 879 break; 880 } 881 return TOKEN; 882 } 883 884 885 yylex() 886 { 887 #define ret_token(c) if (bufptr != buffer) { \ 888 save(c); \ 889 *bufptr = 0; \ 890 bufptr = buffer; \ 891 yylval.string = string_lookup(buffer); \ 892 return token_type(buffer); \ 893 } 894 #define save(c) { last_char = c; last_saved = 1; } 895 #if defined(YYDEBUG) 896 #define Return(c) if (yydebug) { \ 897 printf("[%d]", c); \ 898 fflush(stdout); \ 899 } \ 900 yyval.intval = c; \ 901 return c; 902 #else /* defined(YYDEBUG) */ 903 #define Return(y,c) { yylval.intval = c; return y; } 904 #endif /* defined(YYDEBUG) */ 905 906 907 static char buffer[500], *bufptr = buffer; 908 static int eof_found = 0; 909 int c; 910 911 if (eof_found != 0) { 912 eof_found++; 913 if (eof_found > 2) { 914 fprintf(stderr, "End of file ignored.\n"); 915 exit(1); 916 } 917 Return(EOF,0); 918 } 919 while ((c = Getchar()) != EOF) { 920 switch (c) { 921 case '#': 922 ret_token(c); 923 while (((c = Getchar()) != EOF) && (c != '\n')) { 924 ; 925 } 926 save(c); 927 break; 928 case '<': 929 case '?': 930 ret_token(c); 931 Return(MACRO_CHAR, c); 932 case '\t': 933 case ' ': 934 ret_token(c); 935 Return(WHITE_SPACE, c); 936 case '-': 937 case '@': 938 case ':': 939 case ';': 940 case '=': 941 case '$': 942 case '{': 943 case '}': 944 case '(': 945 case ')': 946 ret_token(c); 947 Return(c,c); 948 case '\'': 949 case '"': 950 if (bufptr != buffer) { 951 if (bufptr[-1] == '\\') { 952 bufptr[-1] = c; 953 } 954 break; 955 } else { 956 int newc; 957 958 ret_token(c); 959 *bufptr++ = c; 960 while (((newc = Getchar()) != EOF) && (newc != c)) { 961 *bufptr++ = newc; 962 } 963 *bufptr++ = c; 964 *bufptr = 0; 965 bufptr = buffer; 966 yylval.string = string_lookup(buffer); 967 return QUOTED_STRING; 968 } 969 case '\n': 970 if (bufptr != buffer) { 971 if (bufptr[-1] == '\\') { 972 bufptr--; 973 if ((c = Getchar()) != '\t') { 974 yyerror("continuation line doesn't begin with a tab"); 975 save(c); 976 } 977 ret_token(c); 978 Return(WHITE_SPACE, c); 979 } 980 } 981 ret_token(c); 982 Return(NL, 0); 983 default: 984 *bufptr++ = c; 985 break; 986 } 987 } 988 989 eof_found = 1; 990 991 ret_token(' '); 992 Return(EOF, 0); 993 } 994 995 main() 996 { 997 #define YYDEBUG 998 extern int yydebug; 999 1000 null = same_item(string_lookup("")); 1001 newline = same_item(string_lookup("\n")); 1002 blank = same_item(string_lookup(" ")); 1003 cwd_line = same_cat(same_copy(newline), 1004 same_cat(same_item(string_lookup("cd ${CWD}")), 1005 same_copy(newline))); 1006 1007 yyparse(); 1008 1009 do_dump(); 1010 1011 return 0; 1012 } 1013 1014 #if defined(YYDEBUG) 1015 dump_same(same) 1016 same_t *same; 1017 { 1018 same_t *same2; 1019 1020 for (visit(same, same2); !visited(same2); visit_next(same2)) { 1021 printf(same2->string->string); 1022 } 1023 visit_end(); 1024 } 1025 #endif /* YYDEBUG */ 1026 1027 do_dump() 1028 { 1029 string_t *string; 1030 same_t *same, *same2; 1031 1032 if (yydebug > 1) { 1033 printf("strings...\n"); 1034 for (string = strings; string; string = string->next) { 1035 printf("\t%s\n", string->string); 1036 } 1037 } 1038 1039 printf("# variables...\n"); 1040 for (visit(variables, same); !visited(same); visit_next(same)) { 1041 printf("%s =\t", same->string->string); 1042 for (visit(same->value_list, same2); !visited(same2); 1043 visit_next(same2)) { 1044 printf(same2->string->string); 1045 } 1046 visit_end(); 1047 printf("\n"); 1048 } 1049 visit_end(); 1050 1051 printf("\n\n#targets...\n"); 1052 for (visit(targets, same); !visited(same); visit_next(same)) { 1053 printf("\n%s:\t", same->string->string); 1054 for (visit(same->depend_list, same2); !visited(same2); 1055 visit_next(same2)) { 1056 printf(same2->string->string); 1057 } 1058 visit_end(); 1059 printf("\n\t"); 1060 for (visit(same->action_list, same2); !visited(same2); 1061 visit_next(same2)) { 1062 printf(same2->string->string); 1063 if (same2->string->string[0] == '\n') { 1064 printf("\t"); 1065 } 1066 } 1067 visit_end(); 1068 printf("\n"); 1069 } 1070 visit_end(); 1071 } 1072