|
|
1.1 ! root 1: /* ! 2: * RCS file input ! 3: */ ! 4: static char rcsid[]= ! 5: "$Header: /usr/wft/RCS/SRC/RCS/rcslex.c,v 3.3 82/12/10 16:22:37 wft Exp $ Purdue CS"; ! 6: /********************************************************************************* ! 7: * Lexical Analysis. ! 8: * Character mapping table, ! 9: * hashtable, Lexinit, nextlex, getlex, getkey, ! 10: * getid, getnum, readstring, printstring, savestring, ! 11: * checkid, serror, fatserror, error, faterror, warn, diagnose ! 12: * fflsbuf, puts, fprintf ! 13: * Testprogram: define LEXDB ! 14: ********************************************************************************* ! 15: * ! 16: * Copyright (C) 1982 by Walter F. Tichy ! 17: * Purdue University ! 18: * Computer Science Department ! 19: * West Lafayette, IN 47907 ! 20: * ! 21: * All rights reserved. No part of this software may be sold or distributed ! 22: * in any form or by any means without the prior written permission of the ! 23: * author. ! 24: * Report problems and direct all inquiries to Tichy@purdue (ARPA net). ! 25: */ ! 26: ! 27: /* $Log: rcslex.c,v $ ! 28: * Revision 3.3 82/12/10 16:22:37 wft ! 29: * Improved error messages, changed exit status on error to 1. ! 30: * ! 31: * Revision 3.2 82/11/28 21:27:10 wft ! 32: * Renamed ctab to map and included EOFILE; ctab is now a macro in rcsbase.h. ! 33: * Added fflsbuf(), fputs(), and fprintf(), which abort the RCS operations ! 34: * properly in case there is an IO-error (e.g., file system full). ! 35: * ! 36: * Revision 3.1 82/10/11 19:43:56 wft ! 37: * removed unused label out:; ! 38: * made sure all calls to getc() return into an integer, not a char. ! 39: */ ! 40: ! 41: ! 42: /* ! 43: #define LEXDB ! 44: /* version LEXDB is for testing the lexical analyzer. The testprogram ! 45: * reads a stream of lexemes, enters the revision numbers into the ! 46: * hashtable, and prints the recognized tokens. Keywords are recognized ! 47: * as identifiers. ! 48: */ ! 49: ! 50: ! 51: ! 52: #include "rcsbase.h" ! 53: ! 54: ! 55: ! 56: /* character mapping table */ ! 57: enum tokens map[] = { ! 58: EOFILE, /* this will end up at ctab[-1] */ ! 59: UNKN, INSERT, UNKN, UNKN, UNKN, UNKN, UNKN, UNKN, ! 60: UNKN, SPACE, NEWLN, UNKN, SPACE, UNKN, UNKN, UNKN, ! 61: UNKN, UNKN, UNKN, UNKN, UNKN, UNKN, UNKN, UNKN, ! 62: UNKN, UNKN, UNKN, UNKN, UNKN, UNKN, UNKN, UNKN, ! 63: SPACE, EXCLA, DQUOTE, HASH, DOLLAR, PERCNT, AMPER, SQUOTE, ! 64: LPARN, RPARN, TIMES, PLUS, COMMA, MINUS, PERIOD, DIVIDE, ! 65: DIGIT, DIGIT, DIGIT, DIGIT, DIGIT, DIGIT, DIGIT, DIGIT, ! 66: DIGIT, DIGIT, COLON, SEMI, LESS, EQUAL, GREAT, QUEST, ! 67: AT, LETTER, LETTER, LETTER, LETTER, LETTER, LETTER, LETTER, ! 68: LETTER, LETTER, LETTER, LETTER, LETTER, LETTER, LETTER, LETTER, ! 69: LETTER, LETTER, LETTER, LETTER, LETTER, LETTER, LETTER, LETTER, ! 70: LETTER, LETTER, LETTER, LBRACK, BACKSL, RBRACK, UPARR, UNDER, ! 71: ACCENT, LETTER, LETTER, LETTER, LETTER, LETTER, LETTER, LETTER, ! 72: LETTER, LETTER, LETTER, LETTER, LETTER, LETTER, LETTER, LETTER, ! 73: LETTER, LETTER, LETTER, LETTER, LETTER, LETTER, LETTER, LETTER, ! 74: LETTER, LETTER, LETTER, LBRACE, BAR, RBRACE, TILDE, UNKN ! 75: }; ! 76: ! 77: ! 78: ! 79: ! 80: struct hshentry * nexthsh; /*pointer to next hashtable-entry, set by lookup*/ ! 81: ! 82: enum tokens nexttok; /*next token, set by nextlex */ ! 83: ! 84: int hshenter /*if true, next suitable lexeme will be entered */ ! 85: = true; /*into the symbol table. Handle with care. */ ! 86: int nextc; /*next input character, initialized by Lexinit */ ! 87: ! 88: int eof /*end-of-file indicator, set to >0 on end of file*/ ! 89: = 0; ! 90: int line /*current line-number of input */ ! 91: = 1; ! 92: int nerror /*counter for errors */ ! 93: = 0; ! 94: int nwarn /*counter for warnings */ ! 95: = 0; ! 96: char * cmdid /*command identification for error messages */ ! 97: = nil; ! 98: int quietflag /*indicates quiet mode */ ! 99: = false; ! 100: FILE * finptr; /*input file descriptor */ ! 101: ! 102: FILE * frewrite; /*file descriptor for echoing input */ ! 103: ! 104: int rewriteflag;/*indicates whether to echo to frewrite */ ! 105: ! 106: char StringTab[strtsize]; /* string table and heap */ ! 107: ! 108: char * NextString /*pointer to next identifier in StringTab*/ ! 109: = nil; ! 110: char * Topchar /*pointer to next free byte in StringTab*/ ! 111: = &StringTab[0]; /*set by nextlex, lookup */ ! 112: struct hshentry hshtab[hshsize]; /*hashtable */ ! 113: ! 114: ! 115: ! 116: ! 117: ! 118: lookup() { ! 119: ! 120: /* Function: Looks up the character string pointed to by NextString in the ! 121: * hashtable. If the string is not present, a new entry for it is created. ! 122: * If the string is present, TopChar is moved back to save the space for ! 123: * the string, and NextString is set to point to the original string. ! 124: * In any case, the address of the corresponding hashtable entry is placed ! 125: * into nexthsh. ! 126: * Algorithm: Quadratic hash, covering all entries. ! 127: * Assumptions: NextString points at the first character of the string. ! 128: * Topchar points at the first empty byte after the string. ! 129: */ ! 130: ! 131: register int ihash; /* index into hashtable */ ! 132: register char * sp, * np; ! 133: int c, delta, final, FirstScan; /*loop control*/ ! 134: ! 135: /* calculate hash code */ ! 136: sp = NextString; ! 137: ihash = 0; ! 138: while (*sp) ihash += *sp++; ! 139: ! 140: /* set up first search loop (c=0,step=1,until (hshsiz-1)/2 */ ! 141: c=0;delta=1;final=(hshsize-1)/2; ! 142: FirstScan=true; /*first loop */ ! 143: ! 144: for (;;) { ! 145: ihash = (ihash+c)%hshsize; /*next index*/ ! 146: ! 147: if (hshtab[ihash].num == nil) { ! 148: /*empty slot found*/ ! 149: hshtab[ihash].num = NextString; ! 150: nexthsh= &hshtab[ihash];/*save hashtable address*/ ! 151: # ifdef LEXDB ! 152: printf("\nEntered: %s at %d ",nexthsh->num, ihash); ! 153: # endif ! 154: return; ! 155: } ! 156: /* compare strings */ ! 157: sp=NextString;np=hshtab[ihash].num; ! 158: while (*sp == *np++) { ! 159: if (*sp == 0) { ! 160: /* match found */ ! 161: nexthsh= &hshtab[ihash]; ! 162: Topchar = NextString; ! 163: NextString = nexthsh->num; ! 164: return; ! 165: } else sp++; ! 166: } ! 167: ! 168: /* neither empty slot nor string found */ ! 169: /* calculate next index and repeat */ ! 170: if (c != final) ! 171: c += delta; ! 172: else { ! 173: if (FirstScan) { ! 174: /*set up second sweep*/ ! 175: delta = -1; final = 1; FirstScan= false; ! 176: } else { ! 177: fatserror("Hashtable overflow"); ! 178: } ! 179: } ! 180: } ! 181: }; ! 182: ! 183: ! 184: ! 185: ! 186: ! 187: ! 188: Lexinit() ! 189: /* Function: Initialization of lexical analyzer: ! 190: * initializes the hastable, ! 191: * initializes nextc, nexttok if finptr != NULL ! 192: */ ! 193: { register int i; ! 194: ! 195: for (i=hshsize-1; i>=0; i--) { ! 196: hshtab[i].num = nil; ! 197: } ! 198: ! 199: hshenter=true; eof=0; line=1; nerror=0; nwarn=0; ! 200: NextString=nil; Topchar = &StringTab[0]; ! 201: if (finptr) { ! 202: nextc = GETC(finptr,frewrite,rewriteflag); /*initial character*/ ! 203: nextlex(); /*initial token*/ ! 204: } else { ! 205: nextc = '\0'; ! 206: nexttok=EOFILE; ! 207: } ! 208: } ! 209: ! 210: ! 211: ! 212: ! 213: ! 214: ! 215: ! 216: nextlex() ! 217: ! 218: /* Function: Reads the next token and sets nexttok to the next token code. ! 219: * Only if the hshenter==true, a revision number is entered into the ! 220: * hashtable and a pointer to it is placed into nexthsh. ! 221: * This is useful for avoiding that dates are placed into the hashtable. ! 222: * For ID's and NUM's, NextString is set to the character string in the ! 223: * string table. Assumption: nextc contains the next character. ! 224: */ ! 225: { register c; ! 226: register char * sp; ! 227: register enum tokens d; ! 228: ! 229: if (eof) { ! 230: nexttok=EOFILE; ! 231: return; ! 232: } ! 233: loop: ! 234: switch(nexttok=ctab[nextc]) { ! 235: ! 236: case UNKN: ! 237: case IDCHAR: ! 238: case PERIOD: ! 239: serror("unknown Character: %c",nextc); ! 240: nextc=GETC(finptr,frewrite,rewriteflag); ! 241: goto loop; ! 242: ! 243: case NEWLN: ! 244: line++; ! 245: # ifdef LEXDB ! 246: putchar('\n'); ! 247: # endif ! 248: /* Note: falls into next case */ ! 249: ! 250: case SPACE: ! 251: nextc=GETC(finptr,frewrite,rewriteflag); ! 252: goto loop; ! 253: ! 254: case EOFILE: ! 255: eof++; ! 256: nexttok=EOFILE; ! 257: return; ! 258: ! 259: case DIGIT: ! 260: NextString = sp = Topchar; ! 261: *sp++ = nextc; ! 262: while ((d=ctab[c=GETC(finptr,frewrite,rewriteflag)])==DIGIT || ! 263: d==PERIOD) { ! 264: *sp++ = c; /* 1.2. and 1.2 are different */ ! 265: } ! 266: *sp++ = '\0'; ! 267: if (sp >= StringTab+strtsize) { ! 268: /*may have written outside stringtable already*/ ! 269: fatserror("Stringtable overflow"); ! 270: } ! 271: Topchar = sp; ! 272: nextc = c; ! 273: if (hshenter == true) ! 274: lookup(); /* lookup updates NextString, Topchar*/ ! 275: nexttok = NUM; ! 276: return; ! 277: ! 278: ! 279: case LETTER: ! 280: NextString = sp = Topchar; ! 281: *sp++ = nextc; ! 282: while ((d=ctab[c=GETC(finptr,frewrite,rewriteflag)])==LETTER || ! 283: d==DIGIT || d==IDCHAR) { ! 284: *sp++ = c; ! 285: } ! 286: *sp++ = '\0'; ! 287: if (sp >= StringTab+strtsize) { ! 288: /*may have written outside stringtable already*/ ! 289: fatserror("Stringtable overflow"); ! 290: } ! 291: Topchar = sp; ! 292: nextc = c; ! 293: nexttok = ID; /* may be ID or keyword */ ! 294: return; ! 295: ! 296: case SBEGIN: /* long string */ ! 297: nexttok = STRING; ! 298: /* note: only the initial SBEGIN has been read*/ ! 299: /* read the string, and reset nextc afterwards*/ ! 300: return; ! 301: ! 302: default: ! 303: nextc=GETC(finptr,frewrite,rewriteflag); ! 304: return; ! 305: } ! 306: } ! 307: ! 308: ! 309: int getlex(token) ! 310: enum tokens token; ! 311: /* Function: Checks if nexttok is the same as token. If so, ! 312: * advances the input by calling nextlex and returns true. ! 313: * otherwise returns false. ! 314: * Doesn't work for strings and keywords; loses the character string for ids. ! 315: */ ! 316: { ! 317: if (nexttok==token) { ! 318: nextlex(); ! 319: return(true); ! 320: } else return(false); ! 321: } ! 322: ! 323: int getkey (key) ! 324: char * key; ! 325: /* Function: If the current token is a keyword identical to key, ! 326: * getkey advances the input by calling nextlex and returns true; ! 327: * otherwise returns false. ! 328: */ ! 329: { ! 330: register char *s1,*s2; ! 331: ! 332: if (nexttok==ID) { ! 333: s1=key; s2=NextString; ! 334: while(*s1 == *s2++) ! 335: if (*s1++ == '\0') { ! 336: /* match found */ ! 337: Topchar = NextString; /*reset Topchar */ ! 338: nextlex(); ! 339: return(true); ! 340: } ! 341: } ! 342: return(false); ! 343: } ! 344: ! 345: ! 346: ! 347: char * getid() ! 348: /* Function: Checks if nexttok is an identifier. If so, ! 349: * advances the input by calling nextlex and returns a pointer ! 350: * to the identifier; otherwise returns nil. ! 351: * Treats keywords as identifiers. ! 352: */ ! 353: { ! 354: register char * name; ! 355: if (nexttok==ID) { ! 356: name = NextString; ! 357: nextlex(); ! 358: return name; ! 359: } else return nil; ! 360: } ! 361: ! 362: ! 363: struct hshentry * getnum() ! 364: /* Function: Checks if nexttok is a number. If so, ! 365: * advances the input by calling nextlex and returns a pointer ! 366: * to the hashtable entry. Otherwise returns nil. ! 367: * Doesn't work if hshenter is false. ! 368: */ ! 369: { ! 370: register struct hshentry * num; ! 371: if (nexttok==NUM) { ! 372: num=nexthsh; ! 373: nextlex(); ! 374: return num; ! 375: } else return nil; ! 376: } ! 377: ! 378: ! 379: readstring() ! 380: /* skip over characters until terminating single SDELIM */ ! 381: /* if rewriteflag==true, copy every character read to frewrite.*/ ! 382: /* Does not advance nextlex at the end. */ ! 383: { register c; ! 384: if (rewriteflag) { ! 385: /* copy string verbatim to frewrite */ ! 386: while ((c=putc(getc(finptr),frewrite)) != EOF) { ! 387: if (c==SDELIM) { ! 388: if ((c=putc(getc(finptr),frewrite)) != SDELIM) { ! 389: /* end of string */ ! 390: nextc=c; ! 391: return; ! 392: } ! 393: } ! 394: } ! 395: } else { ! 396: /* skip string */ ! 397: while ((c=getc(finptr)) != EOF) { ! 398: if (c==SDELIM) { ! 399: if ((c=getc(finptr)) != SDELIM) { ! 400: /* end of string */ ! 401: nextc=c; ! 402: return; ! 403: } ! 404: } ! 405: } ! 406: } ! 407: nextc = c; ! 408: error("Unterminated string"); ! 409: } ! 410: ! 411: ! 412: printstring() ! 413: /* Function: copy a string to stdout, until terminated with a single SDELIM. ! 414: * Does not advance nextlex at the end. ! 415: */ ! 416: { ! 417: register c; ! 418: while ((c=getc(finptr)) != EOF) { ! 419: if (c==SDELIM) { ! 420: if ((c=getc(finptr)) != SDELIM) { ! 421: /* end of string */ ! 422: nextc=c; ! 423: return; ! 424: } ! 425: } ! 426: putchar(c); ! 427: } ! 428: nextc = c; ! 429: error("Unterminated string"); ! 430: } ! 431: ! 432: ! 433: ! 434: int savestring(target,length) ! 435: char * target; int length; ! 436: /* copies a string terminated with SDELIM from file finptr to buffer target, ! 437: * but not more than length bytes. If the string is longer than length, ! 438: * the extra characters are skipped. The string may be empty, in which ! 439: * case a '\0' is placed into target. ! 440: * Double SDELIM is replaced with SDELIM. ! 441: * If rewriteflag==true, the string is also copied unchanged to frewrite. ! 442: * Returns the length of the saved string. ! 443: * Does not advance nextlex at the end. ! 444: */ ! 445: { ! 446: register char * tp, * max; ! 447: register c; ! 448: ! 449: tp=target; max= target+length; /*max is one too large*/ ! 450: while ((c=GETC(finptr,frewrite,rewriteflag))!=EOF) { ! 451: *tp++ =c; ! 452: if (c== SDELIM) { ! 453: if ((c=GETC(finptr,frewrite,rewriteflag))!=SDELIM) { ! 454: /* end of string */ ! 455: *(tp-1)='\0'; ! 456: nextc=c; ! 457: return tp-target; ! 458: } ! 459: } ! 460: if (tp >= max) { ! 461: /* overflow */ ! 462: error("string buffer overflow -- truncating string"); ! 463: target[length-1]='\0'; ! 464: /* skip rest of string */ ! 465: while ((c=GETC(finptr,frewrite,rewriteflag))!=EOF) { ! 466: if ((c==SDELIM) && ((c=GETC(finptr,frewrite,rewriteflag))!=SDELIM)) { ! 467: /* end of string */ ! 468: nextc=c; ! 469: return length; ! 470: } ! 471: } ! 472: nextc = c; ! 473: error("Can't find %c to terminate string before end of file",SDELIM); ! 474: return length; ! 475: } ! 476: } ! 477: nextc = c; ! 478: error("Can't find %c to terminate string before end of file",SDELIM); ! 479: return length; ! 480: } ! 481: ! 482: ! 483: char *checkid(id, delim) ! 484: char *id, delim; ! 485: /* Function: check whether the string starting at id is an */ ! 486: /* identifier and return a pointer to the last char*/ ! 487: /* of the identifer. White space, delim and '\0' */ ! 488: /* are legal delimeters. Aborts the program if not */ ! 489: /* a legal identifier. Useful for checking commands*/ ! 490: { ! 491: register enum tokens d; ! 492: register char *temp; ! 493: register char c,tc; ! 494: ! 495: temp = id; ! 496: if ( ctab[*id] == LETTER ) { ! 497: while( (d=ctab[c=(*++id)]) == LETTER || d==DIGIT || d==IDCHAR) ; ! 498: if ( c!=' ' && c!='\t' && c!='\n' && c!='\0' && c!=delim) { ! 499: /* append \0 to end of id before error message */ ! 500: tc = c; ! 501: while( (c=(*++id))!=' ' && c!='\t' && c!='\n' && c!='\0' && c!=delim) ; ! 502: *id = '\0'; ! 503: faterror("Invalid character %c in identifier %s",tc,temp); ! 504: return nil ; ! 505: } else ! 506: return id; ! 507: } else { ! 508: /* append \0 to end of id before error message */ ! 509: while( (c=(*++id))!=' ' && c!='\t' && c!='\n' && c!='\0' && c!=delim) ; ! 510: *id = '\0'; ! 511: faterror("Identifier %s does not start with letter",temp); ! 512: return nil; ! 513: } ! 514: } ! 515: ! 516: ! 517: serror(e,e1,e2,e3,e4,e5) ! 518: char * e, * e1; ! 519: /* non-fatal syntax error */ ! 520: { nerror++; ! 521: fprintf(stderr,"%s error, line %d: ", cmdid, line); ! 522: fprintf(stderr,e, e1, e2, e3, e4, e5); ! 523: putc('\n',stderr); ! 524: } ! 525: ! 526: error(e,e1,e2,e3,e4,e5) ! 527: char * e, * e1; ! 528: /* non-fatal error */ ! 529: { nerror++; ! 530: fprintf(stderr,"%s error: ",cmdid); ! 531: fprintf(stderr,e, e1, e2, e3, e4, e5); ! 532: putc('\n',stderr); ! 533: } ! 534: ! 535: fatserror(e,e1,e2,e3,e4,e5) ! 536: char * e, * e1; ! 537: /* fatal syntax error */ ! 538: { nerror++; ! 539: fprintf(stderr,"%s error, line %d: ", cmdid,line); ! 540: fprintf(stderr,e, e1, e2, e3, e4, e5); ! 541: fprintf(stderr,"\n%s aborted\n",cmdid); ! 542: cleanup(); ! 543: exit(1); ! 544: } ! 545: ! 546: faterror(e,e1,e2,e3,e4,e5) ! 547: char * e, * e1; ! 548: /* fatal error, terminates program after cleanup */ ! 549: { nerror++; ! 550: fprintf(stderr,"%s error: ",cmdid); ! 551: fprintf(stderr,e, e1, e2, e3, e4, e5); ! 552: fprintf(stderr,"\n%s aborted\n",cmdid); ! 553: cleanup(); ! 554: exit(1); ! 555: } ! 556: ! 557: warn(e,e1,e2,e3,e4,e5) ! 558: char * e, * e1; ! 559: /* prints a warning message */ ! 560: { nwarn++; ! 561: fprintf(stderr,"%s warning: ",cmdid); ! 562: fprintf(stderr,e, e1, e2, e3, e4, e5); ! 563: putc('\n',stderr); ! 564: } ! 565: ! 566: ! 567: diagnose(e,e1,e2,e3,e4,e5) ! 568: char * e, * e1; ! 569: /* prints a diagnostic message */ ! 570: { ! 571: if (!quietflag) { ! 572: fprintf(stderr,e, e1, e2, e3, e4, e5); ! 573: putc('\n',stderr); ! 574: } ! 575: } ! 576: ! 577: ! 578: ! 579: fflsbuf(c, iop) ! 580: int c; register FILE * iop; ! 581: /* Function: Flush iop. ! 582: * Same routine as _flsbuf in stdio, but aborts program on error. ! 583: */ ! 584: { register result; ! 585: if ((result=_flsbuf(c,iop))==EOF) ! 586: faterror("write error"); ! 587: return result; ! 588: } ! 589: ! 590: ! 591: fputs(s, iop) ! 592: register char *s; ! 593: register FILE *iop; ! 594: /* Function: Put string s on file iop, abort on error. ! 595: * Same as puts in stdio, but with different putc macro. ! 596: */ ! 597: { ! 598: register r; ! 599: register c; ! 600: ! 601: while (c = *s++) ! 602: r = putc(c, iop); ! 603: return(r); ! 604: } ! 605: ! 606: ! 607: ! 608: fprintf(iop, fmt, args) ! 609: FILE *iop; ! 610: char *fmt; ! 611: /* Function: formatted output. Same as fprintf in stdio, ! 612: * but aborts program on error ! 613: */ ! 614: { ! 615: _doprnt(fmt, &args, iop); ! 616: if (ferror(iop)) { ! 617: faterror("write error"); ! 618: return EOF; ! 619: } else return 0; ! 620: } ! 621: ! 622: ! 623: ! 624: #ifdef LEXDB ! 625: /* test program reading a stream of lexems and printing the tokens. ! 626: */ ! 627: ! 628: ! 629: ! 630: main(argc,argv) ! 631: int argc; char * argv[]; ! 632: { ! 633: cmdid="lextest"; ! 634: if (argc<2) { ! 635: fputs("No input file\n",stderr); ! 636: exit(1); ! 637: } ! 638: if ((finptr=fopen(argv[1], "r")) == NULL) { ! 639: faterror("Can't open input file %s\n",argv[1]); ! 640: } ! 641: Lexinit(); ! 642: rewriteflag=false; ! 643: while (nexttok != EOFILE) { ! 644: switch (nexttok) { ! 645: ! 646: case ID: ! 647: printf("ID: %s",NextString); ! 648: break; ! 649: ! 650: case NUM: ! 651: if (hshenter==true) ! 652: printf("NUM: %s, index: %d",nexthsh->num, nexthsh-hshtab); ! 653: else ! 654: printf("NUM, unentered: %s",NextString); ! 655: hshenter = !hshenter; /*alternate between dates and numbers*/ ! 656: break; ! 657: ! 658: case COLON: ! 659: printf("COLON"); break; ! 660: ! 661: case SEMI: ! 662: printf("SEMI"); break; ! 663: ! 664: case STRING: ! 665: readstring(); ! 666: printf("STRING"); break; ! 667: ! 668: case UNKN: ! 669: printf("UNKN"); break; ! 670: ! 671: default: ! 672: printf("DEFAULT"); break; ! 673: } ! 674: printf(" | "); ! 675: nextlex(); ! 676: } ! 677: printf("\nEnd of lexical analyzer test\n"); ! 678: } ! 679: ! 680: cleanup() ! 681: /* dummy */ ! 682: {} ! 683: ! 684: ! 685: #endif
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.