xref: /plan9/sys/src/cmd/spin/main.c (revision 0b459c2cb92b7c9d88818e9a2f72e678e5bc4553)
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