1 /***** spin: main.c *****/ 2 3 /* Copyright (c) 1991-2000 by Lucent Technologies - Bell Laboratories */ 4 /* All Rights Reserved. This software is for educational purposes only. */ 5 /* Permission is given to distribute this code provided that this intro- */ 6 /* ductory message is not removed and no monies are exchanged. */ 7 /* No guarantee is expressed or implied by the distribution of this code. */ 8 /* Software written by Gerard J. Holzmann as part of the book: */ 9 /* `Design and Validation of Computer Protocols,' ISBN 0-13-539925-4, */ 10 /* Prentice Hall, Englewood Cliffs, NJ, 07632. */ 11 /* Send bug-reports and/or questions to: gerard@research.bell-labs.com */ 12 13 #include <stdlib.h> 14 #include "spin.h" 15 #include "version.h" 16 #include <signal.h> 17 /* #include <malloc.h> */ 18 #include <time.h> 19 #ifdef PC 20 #include <io.h> 21 #include "y_tab.h" 22 #else 23 #include <unistd.h> 24 #include "y.tab.h" 25 #endif 26 27 extern int DstepStart, lineno, tl_terse; 28 extern FILE *yyin, *yyout, *tl_out; 29 extern Symbol *context; 30 extern char *claimproc; 31 extern void qhide(int); 32 33 Symbol *Fname, *oFname; 34 35 int Etimeouts=0; /* nr timeouts in program */ 36 int Ntimeouts=0; /* nr timeouts in never claim */ 37 int analyze=0, columns=0, dumptab=0, has_remote=0; 38 int interactive=0, jumpsteps=0, m_loss=0, nr_errs=0; 39 int s_trail=0, ntrail=0, verbose=0, xspin=0, no_print=0, no_wrapup = 0, Caccess=0; 40 int limited_vis=0, like_java=0; 41 int dataflow = 1, merger = 1, deadvar = 1, rvopt = 0, ccache = 1; 42 int separate = 0; /* separate compilation option */ 43 #if 0 44 meaning of flags on verbose: 45 1 -g global variable values 46 2 -l local variable values 47 4 -p all process actions 48 8 -r receives 49 16 -s sends 50 32 -v verbose 51 64 -w very verbose 52 #endif 53 54 static Element *Same; 55 static int IsAsgn = 0, OrIsAsgn = 0; 56 static char Operator[] = "operator: "; 57 static char Keyword[] = "keyword: "; 58 static char Function[] = "function-name: "; 59 static char **add_ltl = (char **)0; 60 static char **ltl_file = (char **)0; 61 static char **nvr_file = (char **)0; 62 static char *PreArg[64]; 63 static int PreCnt = 0; 64 static char out1[64]; 65 static void explain(int); 66 67 #ifndef CPP 68 /* OS2: "spin -Picc -E/Pd+ -E/Q+" */ 69 /* Visual C++: "spin -PCL -E/E */ 70 #ifdef PC 71 #define CPP "cpp" /* or specify a full path */ 72 #else 73 #ifdef SOLARIS 74 #define CPP "/usr/ccs/lib/cpp" 75 #else 76 #ifdef __FreeBSD__ 77 #define CPP "cpp" 78 #else 79 #define CPP "/bin/cpp" 80 #endif 81 #endif 82 #endif 83 84 #endif 85 static char *PreProc = CPP; 86 87 void 88 alldone(int estatus) 89 { 90 if (strlen(out1) > 0) 91 (void) unlink((const char *)out1); 92 if (xspin && (analyze || s_trail)) 93 { if (estatus) 94 printf("spin: %d error(s) - aborting\n", 95 estatus); 96 else 97 printf("Exit-Status 0\n"); 98 } 99 exit(estatus); 100 } 101 102 void 103 preprocess(char *a, char *b, int a_tmp) 104 { char precmd[128], cmd[256]; int i; 105 #ifdef PC 106 extern int try_zpp(char *, char *); 107 if (try_zpp(a, b)) goto out; 108 #endif 109 strcpy(precmd, PreProc); 110 for (i = 1; i <= PreCnt; i++) 111 { strcat(precmd, " "); 112 strcat(precmd, PreArg[i]); 113 } 114 sprintf(cmd, "%s %s > %s", precmd, a, b); 115 if (system((const char *)cmd)) 116 { (void) unlink((const char *) b); 117 if (a_tmp) (void) unlink((const char *) a); 118 alldone(1); /* failed */ 119 } 120 out: if (a_tmp) (void) unlink((const char *) a); 121 } 122 123 FILE * 124 cpyfile(char *src, char *tgt) 125 { FILE *inp, *out; 126 char buf[1024]; 127 128 inp = fopen(src, "r"); 129 out = fopen(tgt, "w"); 130 if (!inp || !out) 131 { printf("spin: cannot cp %s to %s\n", src, tgt); 132 alldone(1); 133 } 134 while (fgets(buf, 1024, inp)) 135 fprintf(out, "%s", buf); 136 fclose(inp); 137 return out; 138 } 139 140 void 141 usage(void) 142 { 143 printf("use: spin [-option] ... [-option] file\n"); 144 printf("\tNote: file must always be the last argument\n"); 145 printf("\t-a generate a verifier in pan.c\n"); 146 printf("\t-B no final state details in simulations\n"); 147 printf("\t-b don't execute printfs in simulation\n"); 148 printf("\t-C print channel access info (structview)\n"); 149 printf("\t-c columnated -s -r simulation output\n"); 150 printf("\t-d produce symbol-table information\n"); 151 printf("\t-Dyyy pass -Dyyy to the preprocessor\n"); 152 printf("\t-Eyyy pass yyy to the preprocessor\n"); 153 printf("\t-f \"..formula..\" translate LTL "); 154 printf("into never claim\n"); 155 printf("\t-F file like -f, but with the LTL "); 156 printf("formula stored in a 1-line file\n"); 157 printf("\t-g print all global variables\n"); 158 printf("\t-i interactive (random simulation)\n"); 159 printf("\t-J reverse eval order of nested unlesses\n"); 160 printf("\t-jN skip the first N steps "); 161 printf("in simulation trail\n"); 162 printf("\t-l print all local variables\n"); 163 printf("\t-M print msc-flow in Postscript\n"); 164 printf("\t-m lose msgs sent to full queues\n"); 165 printf("\t-N file use never claim stored in file\n"); 166 printf("\t-nN seed for random nr generator\n"); 167 printf("\t-o1 turn off dataflow-optimizations in verifier\n"); 168 printf("\t-o2 turn off dead variables elimination in verifier\n"); 169 printf("\t-o3 turn off statement merging in verifier\n"); 170 printf("\t-Pxxx use xxx for preprocessing\n"); 171 printf("\t-p print all statements\n"); 172 printf("\t-qN suppress io for queue N in printouts\n"); 173 printf("\t-r print receive events\n"); 174 printf("\t-s print send events\n"); 175 printf("\t-v verbose, more warnings\n"); 176 printf("\t-w very verbose (when combined with -l or -g)\n"); 177 printf("\t-t[N] follow [Nth] simulation trail\n"); 178 printf("\t-[XYZ] reserved for use by xspin interface\n"); 179 printf("\t-V print version number and exit\n"); 180 alldone(1); 181 } 182 183 void 184 optimizations(char nr) 185 { 186 switch (nr) { 187 case '1': 188 dataflow = 1 - dataflow; /* dataflow */ 189 if (verbose&32) 190 printf("spin: dataflow optimizations turned %s\n", 191 dataflow?"on":"off"); 192 break; 193 case '2': 194 /* dead variable elimination */ 195 deadvar = 1 - deadvar; 196 if (verbose&32) 197 printf("spin: dead variable elimination turned %s\n", 198 deadvar?"on":"off"); 199 break; 200 case '3': 201 /* statement merging */ 202 merger = 1 - merger; 203 if (verbose&32) 204 printf("spin: statement merging turned %s\n", 205 merger?"on":"off"); 206 break; 207 208 case '4': 209 /* rv optimization */ 210 rvopt = 1 - rvopt; 211 if (verbose&32) 212 printf("spin: rendezvous optimization turned %s\n", 213 rvopt?"on":"off"); 214 break; 215 case '5': 216 /* case caching */ 217 ccache = 1 - ccache; 218 if (verbose&32) 219 printf("spin: case caching turned %s\n", 220 ccache?"on":"off"); 221 break; 222 default: 223 printf("spin: bad or missing parameter on -o\n"); 224 usage(); 225 break; 226 } 227 } 228 229 int 230 main(int argc, char *argv[]) 231 { Symbol *s; int preprocessonly = 0; 232 int T = (int) time((time_t *)0); 233 int usedopts = 0; 234 extern void ana_src(int, int); 235 236 yyin = stdin; 237 yyout = stdout; 238 tl_out = stdout; 239 240 /* unused flags: e, w, x, y, z, A, G, I, L, O, Q, R, S, T, W */ 241 while (argc > 1 && argv[1][0] == '-') 242 { switch (argv[1][1]) { 243 244 /* generate code for separate compilation: S1 or S2 */ 245 case 'S': separate = atoi(&argv[1][2]); 246 /* fall through */ 247 248 case 'a': analyze = 1; break; 249 case 'B': no_wrapup = 1; break; 250 case 'b': no_print = 1; break; 251 case 'C': Caccess = 1; break; 252 case 'c': columns = 1; break; 253 case 'D': PreArg[++PreCnt] = (char *) &argv[1][0]; 254 break; 255 case 'd': dumptab = 1; break; 256 case 'E': PreArg[++PreCnt] = (char *) &argv[1][2]; 257 break; 258 case 'F': ltl_file = (char **) (argv+2); 259 argc--; argv++; break; 260 case 'f': add_ltl = (char **) argv; 261 argc--; argv++; break; 262 case 'g': verbose += 1; break; 263 case 'i': interactive = 1; break; 264 case 'J': like_java = 1; break; 265 case 'j': jumpsteps = atoi(&argv[1][2]); break; 266 case 'l': verbose += 2; break; 267 case 'M': columns = 2; break; 268 case 'm': m_loss = 1; break; 269 case 'N': nvr_file = (char **) (argv+2); 270 argc--; argv++; break; 271 case 'n': T = atoi(&argv[1][2]); tl_terse = 1; break; 272 case 'o': optimizations(argv[1][2]); 273 usedopts = 1; break; 274 case 'P': PreProc = (char *) &argv[1][2]; break; 275 case 'p': verbose += 4; break; 276 case 'q': if (isdigit(argv[1][2])) 277 qhide(atoi(&argv[1][2])); 278 break; 279 case 'r': verbose += 8; break; 280 case 's': verbose += 16; break; 281 case 't': s_trail = 1; 282 if (isdigit(argv[1][2])) 283 ntrail = atoi(&argv[1][2]); 284 break; 285 case 'v': verbose += 32; break; 286 case 'V': printf("%s\n", Version); 287 alldone(0); 288 break; 289 case 'w': verbose += 64; break; 290 case 'X': xspin = 1; 291 #ifndef PC 292 signal(SIGPIPE, alldone); /* not posix... */ 293 #endif 294 break; 295 case 'Y': limited_vis = 1; break; /* used by xspin */ 296 case 'Z': preprocessonly = 1; break; /* used by xspin */ 297 298 default : usage(); break; 299 } 300 argc--, argv++; 301 } 302 if (usedopts && !analyze) 303 printf("spin: warning -o[123] option ignored in simulations\n"); 304 305 if (ltl_file) 306 { char formula[4096]; 307 add_ltl = ltl_file-2; add_ltl[1][1] = 'f'; 308 if (!(tl_out = fopen(*ltl_file, "r"))) 309 { printf("spin: cannot open %s\n", *ltl_file); 310 alldone(1); 311 } 312 fgets(formula, 4096, tl_out); 313 fclose(tl_out); 314 tl_out = stdout; 315 *ltl_file = (char *) formula; 316 } 317 if (argc > 1) 318 { char cmd[128], out2[64]; 319 #ifdef PC 320 strcpy(out1, "_tmp1_"); 321 strcpy(out2, "_tmp2_"); 322 #else 323 /* extern char *tmpnam(char *); in stdio.h */ 324 if (add_ltl || nvr_file) 325 { /* must remain in current dir */ 326 strcpy(out1, "_tmp1_"); 327 strcpy(out2, "_tmp2_"); 328 } else 329 { (void) tmpnam(out1); 330 (void) tmpnam(out2); 331 } 332 #endif 333 if (add_ltl) 334 { tl_out = cpyfile(argv[1], out2); 335 nr_errs = tl_main(2, add_ltl); /* in tl_main.c */ 336 fclose(tl_out); 337 preprocess(out2, out1, 1); 338 } else if (nvr_file) 339 { FILE *fd; char buf[1024]; 340 341 if ((fd = fopen(*nvr_file, "r")) == NULL) 342 { printf("spin: cannot open %s\n", 343 *nvr_file); 344 alldone(1); 345 } 346 tl_out = cpyfile(argv[1], out2); 347 while (fgets(buf, 1024, fd)) 348 fprintf(tl_out, "%s", buf); 349 fclose(tl_out); 350 fclose(fd); 351 preprocess(out2, out1, 1); 352 } else 353 preprocess(argv[1], out1, 0); 354 355 if (preprocessonly) 356 { (void) unlink("pan.pre"); /* remove old version */ 357 if (rename((const char *) out1, "pan.pre") != 0) 358 { printf("spin: rename %s failed\n", out1); 359 alldone(1); 360 } 361 alldone(0); 362 } 363 if (!(yyin = fopen(out1, "r"))) 364 { printf("spin: cannot open %s\n", out1); 365 alldone(1); 366 } 367 368 if (strncmp(argv[1], "progress", 8) == 0 369 || strncmp(argv[1], "accept", 6) == 0) 370 sprintf(cmd, "_%s", argv[1]); 371 else 372 strcpy(cmd, argv[1]); 373 oFname = Fname = lookup(cmd); 374 if (oFname->name[0] == '\"') 375 { int i = strlen(oFname->name); 376 oFname->name[i-1] = '\0'; 377 oFname = lookup(&oFname->name[1]); 378 } 379 } else 380 { oFname = Fname = lookup("<stdin>"); 381 if (add_ltl) 382 { if (argc > 0) 383 exit(tl_main(2, add_ltl)); 384 printf("spin: missing argument to -f\n"); 385 alldone(1); 386 } 387 printf("%s\n", Version); 388 printf("reading input from stdin:\n"); 389 fflush(stdout); 390 } 391 if (columns == 2) 392 { extern void putprelude(void); 393 if (xspin || verbose&(1|4|8|16|32)) 394 { printf("spin: -c precludes all flags except -t\n"); 395 alldone(1); 396 } 397 putprelude(); 398 } 399 if (columns && !(verbose&8) && !(verbose&16)) 400 verbose += (8+16); 401 if (columns == 2 && limited_vis) 402 verbose += (1+4); 403 Srand(T); /* defined in run.c */ 404 s = lookup("_"); s->type = PREDEF; /* a write only global var */ 405 s = lookup("_p"); s->type = PREDEF; 406 s = lookup("_pid"); s->type = PREDEF; 407 s = lookup("_last"); s->type = PREDEF; 408 s = lookup("_nr_pr"); s->type = PREDEF; /* new 3.3.10 */ 409 yyparse(); 410 fclose(yyin); 411 chanaccess(); 412 if (!s_trail && (dataflow || merger)) 413 ana_src(dataflow, merger); 414 sched(); 415 alldone(nr_errs); 416 return 0; /* can't get here */ 417 } 418 419 int 420 yywrap(void) /* dummy routine */ 421 { 422 return 1; 423 } 424 425 void 426 non_fatal(char *s1, char *s2) 427 { extern char yytext[]; 428 429 printf("spin: line %3d %s, Error: ", 430 lineno, Fname?Fname->name:"nofilename"); 431 if (s2) 432 printf(s1, s2); 433 else 434 printf(s1); 435 #if 0 436 if (yychar != -1 && yychar != 0) 437 { printf(" saw '"); 438 explain(yychar); 439 printf("'"); 440 } 441 #endif 442 if (yytext && strlen(yytext)>1) 443 printf(" near '%s'", yytext); 444 printf("\n"); 445 nr_errs++; 446 } 447 448 void 449 fatal(char *s1, char *s2) 450 { 451 non_fatal(s1, s2); 452 alldone(1); 453 } 454 455 char * 456 emalloc(int n) 457 { char *tmp; 458 459 if (!(tmp = (char *) malloc(n))) 460 fatal("not enough memory", (char *)0); 461 memset(tmp, 0, n); 462 return tmp; 463 } 464 465 void 466 trapwonly(Lextok *n, char *s) 467 { extern int realread; 468 short i = (n->sym)?n->sym->type:0; 469 470 if (i != MTYPE 471 && i != BIT 472 && i != BYTE 473 && i != SHORT 474 && i != INT 475 && i != UNSIGNED) 476 return; 477 478 if (realread) 479 n->sym->hidden |= 32; /* var is read at least once */ 480 } 481 482 void 483 setaccess(Symbol *sp, Symbol *what, int cnt, int t) 484 { Access *a; 485 486 for (a = sp->access; a; a = a->lnk) 487 if (a->who == context 488 && a->what == what 489 && a->cnt == cnt 490 && a->typ == t) 491 return; 492 493 a = (Access *) emalloc(sizeof(Access)); 494 a->who = context; 495 a->what = what; 496 a->cnt = cnt; 497 a->typ = t; 498 a->lnk = sp->access; 499 sp->access = a; 500 } 501 502 Lextok * 503 nn(Lextok *s, int t, Lextok *ll, Lextok *rl) 504 { Lextok *n = (Lextok *) emalloc(sizeof(Lextok)); 505 506 n->ntyp = (short) t; 507 if (s && s->fn) 508 { n->ln = s->ln; 509 n->fn = s->fn; 510 } else if (rl && rl->fn) 511 { n->ln = rl->ln; 512 n->fn = rl->fn; 513 } else if (ll && ll->fn) 514 { n->ln = ll->ln; 515 n->fn = ll->fn; 516 } else 517 { n->ln = lineno; 518 n->fn = Fname; 519 } 520 if (s) n->sym = s->sym; 521 n->lft = ll; 522 n->rgt = rl; 523 n->indstep = DstepStart; 524 525 if (t == TIMEOUT) Etimeouts++; 526 527 if (!context) return n; 528 529 if (t == 'r' || t == 's') 530 setaccess(n->sym, ZS, 0, t); 531 if (t == 'R') 532 setaccess(n->sym, ZS, 0, 'P'); 533 534 if (context->name == claimproc) 535 { int forbidden = separate; 536 switch (t) { 537 case ASGN: 538 printf("spin: Warning, never claim has side-effect\n"); 539 break; 540 case 'r': case 's': 541 non_fatal("never claim contains i/o stmnts",(char *)0); 542 break; 543 case TIMEOUT: 544 /* never claim polls timeout */ 545 if (Ntimeouts && Etimeouts) 546 forbidden = 0; 547 Ntimeouts++; Etimeouts--; 548 break; 549 case LEN: case EMPTY: case FULL: 550 case 'R': case NFULL: case NEMPTY: 551 /* status becomes non-exclusive */ 552 if (n->sym && !(n->sym->xu&XX)) 553 { n->sym->xu |= XX; 554 if (separate == 2) { 555 printf("spin: warning, make sure that the S1 model\n"); 556 printf(" also polls channel '%s' in its claim\n", 557 n->sym->name); 558 } } 559 forbidden = 0; 560 break; 561 default: 562 forbidden = 0; 563 break; 564 } 565 if (forbidden) 566 { printf("spin: never, saw "); explain(t); printf("\n"); 567 fatal("incompatible with separate compilation",(char *)0); 568 } 569 } else if (t == ENABLED || t == PC_VAL) 570 { printf("spin: Warning, using %s outside never-claim\n", 571 (t == ENABLED)?"enabled()":"pc_value()"); 572 } else if (t == NONPROGRESS) 573 { fatal("spin: Error, using np_ outside never-claim\n", (char *)0); 574 } 575 return n; 576 } 577 578 Lextok * 579 rem_lab(Symbol *a, Lextok *b, Symbol *c) 580 { Lextok *tmp1, *tmp2, *tmp3; 581 582 has_remote++; 583 fix_dest(c, a); /* in case target is jump */ 584 tmp1 = nn(ZN, '?', b, ZN); tmp1->sym = a; 585 tmp1 = nn(ZN, 'p', tmp1, ZN); 586 tmp1->sym = lookup("_p"); 587 tmp2 = nn(ZN, NAME, ZN, ZN); tmp2->sym = a; 588 tmp3 = nn(ZN, 'q', tmp2, ZN); tmp3->sym = c; 589 return nn(ZN, EQ, tmp1, tmp3); 590 } 591 592 static void 593 explain(int n) 594 { FILE *fd = stdout; 595 switch (n) { 596 default: if (n > 0 && n < 256) 597 fprintf(fd, "%c' = '", n); 598 fprintf(fd, "%d", n); 599 break; 600 case '\b': fprintf(fd, "\\b"); break; 601 case '\t': fprintf(fd, "\\t"); break; 602 case '\f': fprintf(fd, "\\f"); break; 603 case '\n': fprintf(fd, "\\n"); break; 604 case '\r': fprintf(fd, "\\r"); break; 605 case 'c': fprintf(fd, "condition"); break; 606 case 's': fprintf(fd, "send"); break; 607 case 'r': fprintf(fd, "recv"); break; 608 case 'R': fprintf(fd, "recv poll %s", Operator); break; 609 case '@': fprintf(fd, "@"); break; 610 case '?': fprintf(fd, "(x->y:z)"); break; 611 case ACTIVE: fprintf(fd, "%sactive", Keyword); break; 612 case AND: fprintf(fd, "%s&&", Operator); break; 613 case ASGN: fprintf(fd, "%s=", Operator); break; 614 case ASSERT: fprintf(fd, "%sassert", Function); break; 615 case ATOMIC: fprintf(fd, "%satomic", Keyword); break; 616 case BREAK: fprintf(fd, "%sbreak", Keyword); break; 617 case CLAIM: fprintf(fd, "%snever", Keyword); break; 618 case CONST: fprintf(fd, "a constant"); break; 619 case DECR: fprintf(fd, "%s--", Operator); break; 620 case D_STEP: fprintf(fd, "%sd_step", Keyword); break; 621 case DO: fprintf(fd, "%sdo", Keyword); break; 622 case DOT: fprintf(fd, "."); break; 623 case ELSE: fprintf(fd, "%selse", Keyword); break; 624 case EMPTY: fprintf(fd, "%sempty", Function); break; 625 case ENABLED: fprintf(fd, "%senabled",Function); break; 626 case EQ: fprintf(fd, "%s==", Operator); break; 627 case EVAL: fprintf(fd, "%seval", Function); break; 628 case FI: fprintf(fd, "%sfi", Keyword); break; 629 case FULL: fprintf(fd, "%sfull", Function); break; 630 case GE: fprintf(fd, "%s>=", Operator); break; 631 case GOTO: fprintf(fd, "%sgoto", Keyword); break; 632 case GT: fprintf(fd, "%s>", Operator); break; 633 case IF: fprintf(fd, "%sif", Keyword); break; 634 case INCR: fprintf(fd, "%s++", Operator); break; 635 case INAME: fprintf(fd, "inline name"); break; 636 case INLINE: fprintf(fd, "%sinline", Keyword); break; 637 case INIT: fprintf(fd, "%sinit", Keyword); break; 638 case LABEL: fprintf(fd, "a label-name"); break; 639 case LE: fprintf(fd, "%s<=", Operator); break; 640 case LEN: fprintf(fd, "%slen", Function); break; 641 case LSHIFT: fprintf(fd, "%s<<", Operator); break; 642 case LT: fprintf(fd, "%s<", Operator); break; 643 case MTYPE: fprintf(fd, "%smtype", Keyword); break; 644 case NAME: fprintf(fd, "an identifier"); break; 645 case NE: fprintf(fd, "%s!=", Operator); break; 646 case NEG: fprintf(fd, "%s! (not)",Operator); break; 647 case NEMPTY: fprintf(fd, "%snempty", Function); break; 648 case NFULL: fprintf(fd, "%snfull", Function); break; 649 case NON_ATOMIC: fprintf(fd, "sub-sequence"); break; 650 case NONPROGRESS: fprintf(fd, "%snp_", Function); break; 651 case OD: fprintf(fd, "%sod", Keyword); break; 652 case OF: fprintf(fd, "%sof", Keyword); break; 653 case OR: fprintf(fd, "%s||", Operator); break; 654 case O_SND: fprintf(fd, "%s!!", Operator); break; 655 case PC_VAL: fprintf(fd, "%spc_value",Function); break; 656 case PNAME: fprintf(fd, "process name"); break; 657 case PRINT: fprintf(fd, "%sprintf", Function); break; 658 case PROCTYPE: fprintf(fd, "%sproctype",Keyword); break; 659 case RCV: fprintf(fd, "%s?", Operator); break; 660 case R_RCV: fprintf(fd, "%s??", Operator); break; 661 case RSHIFT: fprintf(fd, "%s>>", Operator); break; 662 case RUN: fprintf(fd, "%srun", Operator); break; 663 case SEP: fprintf(fd, "token: ::"); break; 664 case SEMI: fprintf(fd, ";"); break; 665 case SND: fprintf(fd, "%s!", Operator); break; 666 case STRING: fprintf(fd, "a string"); break; 667 case TIMEOUT: fprintf(fd, "%stimeout",Keyword); break; 668 case TYPE: fprintf(fd, "data typename"); break; 669 case TYPEDEF: fprintf(fd, "%stypedef",Keyword); break; 670 case XU: fprintf(fd, "%sx[rs]", Keyword); break; 671 case UMIN: fprintf(fd, "%s- (unary minus)", Operator); break; 672 case UNAME: fprintf(fd, "a typename"); break; 673 case UNLESS: fprintf(fd, "%sunless", Keyword); break; 674 } 675 } 676