19679Slinton /* Copyright (c) 1982 Regents of the University of California */ 29679Slinton 3*16928Ssam static char sccsid[] = "@(#)scanner.c 1.10 (Berkeley) 08/12/84"; 49679Slinton 59679Slinton /* 69679Slinton * Debugger scanner. 79679Slinton */ 89679Slinton 9*16928Ssam #include <ctype.h> 109679Slinton #include "defs.h" 119679Slinton #include "scanner.h" 129679Slinton #include "main.h" 139679Slinton #include "keywords.h" 149679Slinton #include "tree.h" 159679Slinton #include "symbols.h" 169679Slinton #include "names.h" 179679Slinton #include "y.tab.h" 189679Slinton 199679Slinton #ifndef public 209679Slinton typedef int Token; 219679Slinton #endif 229679Slinton 23*16928Ssam typedef struct { 24*16928Ssam int s_type; 25*16928Ssam #define ST_FILE 0 26*16928Ssam #define ST_ALIAS 1 27*16928Ssam char *s_name; 28*16928Ssam int s_lineno; 29*16928Ssam union { 30*16928Ssam File su_file; 31*16928Ssam struct sum { 32*16928Ssam char *sum_data; 33*16928Ssam char *sum_cur; 34*16928Ssam } su_macro; 35*16928Ssam } su; 36*16928Ssam #define s_file su.su_file 37*16928Ssam #define s_macro su.su_macro 38*16928Ssam #define s_data s_macro.sum_data 39*16928Ssam #define s_cur s_macro.sum_cur 40*16928Ssam } STREAM; 419679Slinton 42*16928Ssam #define NSTREAMS 10 43*16928Ssam private STREAM stack[NSTREAMS]; 44*16928Ssam private STREAM *sp = &stack[-1]; 459679Slinton 46*16928Ssam public String initfile = ".dbxinit"; 479679Slinton 489679Slinton private Token getident(); 499679Slinton private Token getnum(); 509679Slinton private Token getstring(); 519679Slinton private Char charcon(); 529679Slinton 53*16928Ssam #define MAXLINESIZE 1024 54*16928Ssam private Char yytext[MAXLINESIZE]; 55*16928Ssam private Boolean shellmode; 56*16928Ssam private Boolean doaliases; 579679Slinton 589679Slinton public scanner_init() 599679Slinton { 60*16928Ssam register Integer i; 619679Slinton 62*16928Ssam if (sp < stack) 63*16928Ssam (void) pushinput(ST_FILE, nil, stdin); 64*16928Ssam shellmode = false; 65*16928Ssam doaliases = true; 66*16928Ssam errfilename = nil; 67*16928Ssam errlineno = sp->s_lineno = 0; 68*16928Ssam yytext[0] = '\0'; 699679Slinton } 709679Slinton 71*16928Ssam #define MAXDEPTH 25 729679Slinton /* 739679Slinton * Read a single token. 749679Slinton * There are two "modes" of operation: one as in a compiler, 759679Slinton * and one for reading shell-like syntax. 769679Slinton */ 779679Slinton public Token yylex() 789679Slinton { 79*16928Ssam register int c; 80*16928Ssam register char *p; 81*16928Ssam register Token t; 82*16928Ssam static int depth = 0; 839679Slinton 84*16928Ssam depth++; 85*16928Ssam if (depth > MAXDEPTH) { 86*16928Ssam depth = 0; 87*16928Ssam error("Alias loop (maximum %d deep).\n", MAXDEPTH); 889679Slinton } 89*16928Ssam again: 90*16928Ssam do 91*16928Ssam c = getch(); 92*16928Ssam while (c == ' ' || c == '\t'); 93*16928Ssam if (isalpha(c) || c == '_' || c == '$') { 94*16928Ssam t = getident(c); 95*16928Ssam if (t == NAME && doaliases) { 96*16928Ssam p = findalias(yylval.y_name); 97*16928Ssam if (p != nil) { 98*16928Ssam if (lexdebug) 99*16928Ssam fprintf(stderr, "alias %s to \"%s\"\n", 100*16928Ssam ident(yylval.y_name), p); 101*16928Ssam if (!pushinput(ST_ALIAS, "", p)) { 102*16928Ssam unwindinput(ST_ALIAS); 103*16928Ssam error("Alias stack overflow."); 104*16928Ssam } 105*16928Ssam t = yylex(); 106*16928Ssam } 107*16928Ssam } 108*16928Ssam goto done; 1099679Slinton } 110*16928Ssam if (isdigit(c)) { 111*16928Ssam t = shellmode ? getident(c) : getnum(c); 112*16928Ssam goto done; 11312120Slinton } 1149679Slinton switch (c) { 115*16928Ssam 116*16928Ssam case '\n': 1179679Slinton t = '\n'; 118*16928Ssam if (sp->s_lineno != 0) { 119*16928Ssam sp->s_lineno++; 120*16928Ssam if (sp->s_type == ST_FILE) 121*16928Ssam errlineno = sp->s_lineno; 1229679Slinton } 1239679Slinton break; 1249679Slinton 125*16928Ssam case '"': 126*16928Ssam case '\'': 127*16928Ssam t = getstring(c); 1289679Slinton break; 1299679Slinton 130*16928Ssam case '.': 1319679Slinton if (shellmode) { 132*16928Ssam t = getident(c); 133*16928Ssam break; 1349679Slinton } 135*16928Ssam c = getch(); 136*16928Ssam ungetch(c); 137*16928Ssam t = isdigit(c) ? getnum('.') : '.'; 1389679Slinton break; 1399679Slinton 140*16928Ssam case '<': 141*16928Ssam c = getch(); 142*16928Ssam if (shellmode || c != '<') { 143*16928Ssam ungetch(c); 144*16928Ssam t = '<'; 145*16928Ssam } else 146*16928Ssam t = LFORMER; 1479679Slinton break; 1489679Slinton 149*16928Ssam case '>': 150*16928Ssam c = getch(); 151*16928Ssam if (shellmode || c != '>') { 152*16928Ssam ungetch(c); 153*16928Ssam t = '>'; 154*16928Ssam } else 155*16928Ssam t = RFORMER; 1569679Slinton break; 1579679Slinton 158*16928Ssam case '#': 159*16928Ssam c = getch(); 160*16928Ssam if (c != '^') { 161*16928Ssam ungetch(c); 162*16928Ssam t = '#'; 163*16928Ssam } else 164*16928Ssam t = ABSTRACTION; 1659679Slinton break; 1669679Slinton 167*16928Ssam case '-': 1689679Slinton if (shellmode) { 169*16928Ssam t = getident(c); 170*16928Ssam break; 1719679Slinton } 172*16928Ssam c = getch(); 173*16928Ssam if (c != '>') { 174*16928Ssam ungetch(c); 175*16928Ssam t = '-'; 176*16928Ssam } else 177*16928Ssam t = ARROW; 1789679Slinton break; 1799679Slinton 180*16928Ssam case EOF: 1819679Slinton t = 0; 1829679Slinton break; 1839679Slinton 184*16928Ssam default: 185*16928Ssam t = shellmode && index("!&*()[];", c) == nil ? 186*16928Ssam getident(c) : c; 1879679Slinton break; 1889679Slinton } 189*16928Ssam done: 1909679Slinton if (lexdebug) { 191*16928Ssam fprintf(stderr, "token "); 192*16928Ssam print_token(stderr, t); 193*16928Ssam fprintf(stderr, "\n"); 1949679Slinton } 195*16928Ssam depth--; 196*16928Ssam return (t); 1979679Slinton } 1989679Slinton 1999679Slinton /* 200*16928Ssam * Scan an identifier and check to see if it's a keyword. 2019679Slinton */ 202*16928Ssam private Token getident(c) 203*16928Ssam Char c; 2049679Slinton { 205*16928Ssam register Char *p, *q; 206*16928Ssam Token t; 2079679Slinton 208*16928Ssam q = yytext; 209*16928Ssam if (shellmode) { 210*16928Ssam do { 211*16928Ssam *q++ = c; 212*16928Ssam c = getch(); 213*16928Ssam } while (index(" \t\n!&<>*[]();", c) == nil); 2149679Slinton } else { 215*16928Ssam do { 216*16928Ssam *q++ = c; 217*16928Ssam c = getch(); 218*16928Ssam } while (isalnum(c) || c == '_' || c == '$'); 2199679Slinton } 220*16928Ssam ungetch(c); 221*16928Ssam *q = '\0'; 222*16928Ssam yylval.y_name = identname(yytext, false); 223*16928Ssam if (shellmode) 224*16928Ssam return (NAME); 2259679Slinton t = findkeyword(yylval.y_name); 226*16928Ssam return (t == nil ? NAME : t); 2279679Slinton } 2289679Slinton 2299679Slinton /* 2309679Slinton * Scan a number. 2319679Slinton */ 232*16928Ssam private Token getnum(c) 233*16928Ssam Char c; 2349679Slinton { 235*16928Ssam register Char *q; 236*16928Ssam register Token t; 237*16928Ssam Integer base = 10; 2389679Slinton 239*16928Ssam q = yytext; 240*16928Ssam if (c == '0') { 241*16928Ssam c = getch(); 242*16928Ssam if (c == 'x') { 243*16928Ssam base = 16; 244*16928Ssam } else { 245*16928Ssam base = 8; 246*16928Ssam ungetch(c); 247*16928Ssam c = '0'; 248*16928Ssam } 249*16928Ssam } 250*16928Ssam if (base == 16) { 251*16928Ssam while (isdigit(c = getch()) || 252*16928Ssam (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F')) 253*16928Ssam *q++ = c; 2549679Slinton } else { 255*16928Ssam do { 256*16928Ssam *q++ = c; 257*16928Ssam c = getch(); 258*16928Ssam } while (isdigit(c)); 2599679Slinton } 260*16928Ssam if (c == '.') { 2619679Slinton do { 262*16928Ssam *q++ = c; 263*16928Ssam c = getch(); 264*16928Ssam } while (isdigit(c)); 265*16928Ssam if (c == 'e' || c == 'E') { 266*16928Ssam c = getch(); 267*16928Ssam if (c == '+' || c == '-' || isdigit(c)) { 268*16928Ssam *q++ = 'e'; 269*16928Ssam do { 270*16928Ssam *q++ = c; 271*16928Ssam c = getch(); 272*16928Ssam } while (isdigit(c)); 273*16928Ssam } 274*16928Ssam } 275*16928Ssam ungetch(c); 276*16928Ssam *q = '\0'; 277*16928Ssam yylval.y_real = atof(yytext); 278*16928Ssam return (REAL); 2799679Slinton } 280*16928Ssam ungetch(c); 2819679Slinton *q = '\0'; 2829679Slinton switch (base) { 283*16928Ssam 284*16928Ssam case 10: 285*16928Ssam yylval.y_int = atol(yytext); 2869679Slinton break; 2879679Slinton 288*16928Ssam case 8: 289*16928Ssam yylval.y_int = octal(yytext); 2909679Slinton break; 2919679Slinton 292*16928Ssam case 16: 293*16928Ssam yylval.y_int = hex(yytext); 2949679Slinton break; 2959679Slinton 296*16928Ssam default: 2979679Slinton badcaseval(base); 2989679Slinton } 299*16928Ssam return (INT); 3009679Slinton } 3019679Slinton 3029679Slinton /* 3039679Slinton * Convert a string of octal digits to an integer. 3049679Slinton */ 3059679Slinton private int octal(s) 3069679Slinton String s; 3079679Slinton { 308*16928Ssam register Char *p; 309*16928Ssam register Integer n; 3109679Slinton 311*16928Ssam n = 0; 312*16928Ssam for (p = s; *p != '\0'; p++) 313*16928Ssam n = (n << 3) + (*p - '0'); 314*16928Ssam return (n); 3159679Slinton } 3169679Slinton 3179679Slinton /* 3189679Slinton * Convert a string of hexadecimal digits to an integer. 3199679Slinton */ 3209679Slinton private int hex(s) 3219679Slinton String s; 3229679Slinton { 323*16928Ssam register Char *p; 324*16928Ssam register Integer n; 3259679Slinton 326*16928Ssam n = 0; 327*16928Ssam for (p = s; *p != '\0'; p++) { 328*16928Ssam n <<= 4; 329*16928Ssam if (*p >= 'a' && *p <= 'f') 330*16928Ssam n += (*p - 'a' + 10); 331*16928Ssam else if (*p >= 'A' && *p <= 'F') 332*16928Ssam n += (*p - 'A' + 10); 333*16928Ssam else 334*16928Ssam n += (*p - '0'); 3359679Slinton } 336*16928Ssam return (n); 3379679Slinton } 3389679Slinton 3399679Slinton /* 3409679Slinton * Scan a string. 3419679Slinton */ 342*16928Ssam private Token getstring(match) 343*16928Ssam Char match; 3449679Slinton { 345*16928Ssam register Char *q, c; 3469679Slinton 347*16928Ssam q = yytext; 348*16928Ssam for (;;) { 349*16928Ssam c = getch(); 350*16928Ssam if (c == '\n' || c == EOF) { 351*16928Ssam error("Unterminated string."); 352*16928Ssam break; 353*16928Ssam } 354*16928Ssam if (c == match) 355*16928Ssam break; 356*16928Ssam *q++ = charcon(c); 3579679Slinton } 358*16928Ssam *q = '\0'; 359*16928Ssam yylval.y_string = strdup(yytext); 360*16928Ssam return (STRING); 3619679Slinton } 3629679Slinton 3639679Slinton /* 3649679Slinton * Process a character constant. 3659679Slinton * Watch out for backslashes. 3669679Slinton */ 367*16928Ssam private Char charcon(c) 368*16928Ssam Char c; 3699679Slinton { 370*16928Ssam register char *cp; 3719679Slinton 372*16928Ssam if (c == '\\') { 373*16928Ssam c = getch(); 374*16928Ssam if (isdigit(c)) { 375*16928Ssam int v; 376*16928Ssam 377*16928Ssam v = 0; 378*16928Ssam do { 379*16928Ssam v = (v << 3) + (c - '0'); 380*16928Ssam c = getch(); 381*16928Ssam } while (isdigit(c)); 382*16928Ssam ungetch(c); 383*16928Ssam return (v); 384*16928Ssam } 385*16928Ssam for (cp = "f\ft\tb\bn\nr\rv\v"; *cp != c; cp += 2) 386*16928Ssam ; 387*16928Ssam if (*cp != '\0') 388*16928Ssam c = *cp; 3899679Slinton } 390*16928Ssam return (c); 3919679Slinton } 3929679Slinton 3939679Slinton /* 394*16928Ssam * Parser error handling. 3959679Slinton */ 396*16928Ssam public yyerror(s) 3979679Slinton String s; 3989679Slinton { 3999679Slinton 400*16928Ssam if (streq(s, "syntax error")) { 401*16928Ssam beginerrmsg(); 402*16928Ssam fprintf(stderr, "Syntax error"); 403*16928Ssam if (yytext[0] != '\0') 404*16928Ssam fprintf(stderr, " on \"%s\".", yytext); 405*16928Ssam enderrmsg(); 406*16928Ssam return; 4079679Slinton } 408*16928Ssam error(s); 4099679Slinton } 4109679Slinton 4119679Slinton /* 412*16928Ssam * Eat the current line. 413*16928Ssam */ 414*16928Ssam private Char lastc = '\0'; 415*16928Ssam 416*16928Ssam public gobble() 417*16928Ssam { 418*16928Ssam register char c; 419*16928Ssam 420*16928Ssam if (lastc != '\n' && lastc != EOF) 421*16928Ssam while ((c = getch()) != EOF && c != '\n') 422*16928Ssam ; 423*16928Ssam } 424*16928Ssam 425*16928Ssam /* 4269679Slinton * Input file management routines. 4279679Slinton */ 4289679Slinton public setinput(filename) 4299679Slinton Filename filename; 4309679Slinton { 431*16928Ssam File f; 4329679Slinton 433*16928Ssam f = fopen(filename, "r"); 434*16928Ssam if (f == nil) 435*16928Ssam error("%s: Can't open.", filename); 436*16928Ssam if (!pushinput(ST_FILE, filename, f)) { 437*16928Ssam unwindinput(ST_FILE); 438*16928Ssam error("Source file nesting too deep."); 4399679Slinton } 4409679Slinton } 4419679Slinton 442*16928Ssam /* 443*16928Ssam * Send the current line to the shell. 444*16928Ssam */ 445*16928Ssam public shellline() 4469679Slinton { 447*16928Ssam register Char *p, c; 4489679Slinton 449*16928Ssam for (p = yytext; (c = getch()) != EOF && c != '\n'; *p++ = c) 450*16928Ssam ; 451*16928Ssam *p = '\0'; 452*16928Ssam shell(yytext); 453*16928Ssam erecover(); 4549679Slinton } 4559679Slinton 4569679Slinton /* 457*16928Ssam * Read the rest of the current line in "shell mode". 4589679Slinton */ 459*16928Ssam public beginshellmode() 460*16928Ssam { 4619679Slinton 462*16928Ssam shellmode = true; 463*16928Ssam } 464*16928Ssam 465*16928Ssam public endshellmode() 4669679Slinton { 4679679Slinton 468*16928Ssam shellmode = false; 4699679Slinton } 4709679Slinton 471*16928Ssam public stopaliasing() 472*16928Ssam { 473*16928Ssam 474*16928Ssam doaliases = false; 475*16928Ssam } 476*16928Ssam 477*16928Ssam public startaliasing() 478*16928Ssam { 479*16928Ssam 480*16928Ssam doaliases = true; 481*16928Ssam } 482*16928Ssam 4839679Slinton /* 484*16928Ssam * Print out a token for debugging. 4859679Slinton */ 486*16928Ssam public print_token(f, t) 487*16928Ssam File f; 488*16928Ssam Token t; 489*16928Ssam { 4909679Slinton 491*16928Ssam switch (t) { 492*16928Ssam 493*16928Ssam case '\n': 494*16928Ssam fprintf(f, "char '\\n'"); 495*16928Ssam return; 496*16928Ssam 497*16928Ssam case EOF: 498*16928Ssam fprintf(f, "EOF"); 499*16928Ssam return; 500*16928Ssam 501*16928Ssam case NAME: 502*16928Ssam case STRING: 503*16928Ssam fprintf(f, "%s, \"%s\"", keywdstring(t), ident(yylval.y_name)); 504*16928Ssam return; 505*16928Ssam } 506*16928Ssam if (t < 256) 507*16928Ssam fprintf(f, "char '%c'", t); 508*16928Ssam else 509*16928Ssam fprintf(f, "%s", keywdstring(t)); 510*16928Ssam } 511*16928Ssam 512*16928Ssam public int getch() 5139679Slinton { 514*16928Ssam int c; 515*16928Ssam 516*16928Ssam again: 517*16928Ssam switch (sp->s_type) { 518*16928Ssam 519*16928Ssam case ST_FILE: 520*16928Ssam c = getc(sp->s_file); 521*16928Ssam if (c == EOF && isterm(sp->s_file)) { 522*16928Ssam clearerr(sp->s_file); 523*16928Ssam putchar('\n'); 524*16928Ssam c = '\n'; 525*16928Ssam } 526*16928Ssam break; 527*16928Ssam 528*16928Ssam case ST_ALIAS: 529*16928Ssam c = *sp->s_cur++; 530*16928Ssam if (c == '\0') { 531*16928Ssam c = EOF; 532*16928Ssam --sp->s_cur; 533*16928Ssam } 534*16928Ssam break; 535*16928Ssam 536*16928Ssam default: 537*16928Ssam panic("Invalid input stream (type %d) to getch.", 538*16928Ssam sp->s_type); 539*16928Ssam } 540*16928Ssam if (c == EOF && popinput()) 541*16928Ssam goto again; 542*16928Ssam return (lastc = c); 5439679Slinton } 5449679Slinton 545*16928Ssam private int ungetch(c) 546*16928Ssam Char c; 547*16928Ssam { 548*16928Ssam Char uc; 549*16928Ssam 550*16928Ssam if (c != EOF) switch (sp->s_type) { 551*16928Ssam 552*16928Ssam case ST_FILE: 553*16928Ssam uc = ungetc(c, sp->s_file); 554*16928Ssam break; 555*16928Ssam 556*16928Ssam case ST_ALIAS: 557*16928Ssam if (sp->s_cur == sp->s_data) 558*16928Ssam panic("Illegal ungetch on alias."); 559*16928Ssam *--sp->s_cur = c; 560*16928Ssam uc = c; 561*16928Ssam break; 562*16928Ssam 563*16928Ssam default: 564*16928Ssam panic("Invalid input stream (type %d) to ungetch.", 565*16928Ssam sp->s_type); 566*16928Ssam } 567*16928Ssam lastc = '\0'; 568*16928Ssam return (uc); 569*16928Ssam } 570*16928Ssam 5719679Slinton /* 572*16928Ssam * Push the current input stream and 573*16928Ssam * make the supplied stream the current. 5749679Slinton */ 575*16928Ssam /*VARARGS3*/ 576*16928Ssam public pushinput(type, name, info) 577*16928Ssam int type; 578*16928Ssam Filename name; 579*16928Ssam { 5809679Slinton 581*16928Ssam if (sp >= &stack[NSTREAMS]) 582*16928Ssam return (0); 583*16928Ssam ++sp; 584*16928Ssam sp->s_type = type; 585*16928Ssam switch (type) { 586*16928Ssam 587*16928Ssam case ST_FILE: 588*16928Ssam sp->s_file = (File)info; 589*16928Ssam errfilename = sp->s_name = name; 590*16928Ssam errlineno = sp->s_lineno = 1; 591*16928Ssam break; 592*16928Ssam 593*16928Ssam case ST_ALIAS: 594*16928Ssam sp->s_cur = sp->s_data = (char *)info; 595*16928Ssam break; 596*16928Ssam 597*16928Ssam default: 598*16928Ssam panic("Invalid input stream (type %d) to pushinput.", type); 599*16928Ssam } 600*16928Ssam return (1); 601*16928Ssam } 602*16928Ssam 603*16928Ssam public popinput() 6049679Slinton { 6059679Slinton 606*16928Ssam if (sp <= &stack[0]) /* never pop stdin or equivalent */ 607*16928Ssam return (0); 608*16928Ssam if (sp->s_type == ST_FILE && sp->s_file != stdin) 609*16928Ssam fclose(sp->s_file); 610*16928Ssam --sp; 611*16928Ssam if (sp->s_type == ST_FILE) 612*16928Ssam errfilename = sp->s_name; 613*16928Ssam errlineno = sp->s_lineno; 614*16928Ssam return (1); 6159679Slinton } 6169679Slinton 6179679Slinton /* 618*16928Ssam * Unwind the input stack of all input types specified. 619*16928Ssam * This is called to recover from an infinite 620*16928Ssam * loop in alias processing or source file including. 6219679Slinton */ 622*16928Ssam public unwindinput(type) 623*16928Ssam Integer type; 624*16928Ssam { 6259679Slinton 626*16928Ssam while (sp->s_type == type && popinput()) 627*16928Ssam ; 6289679Slinton } 6299679Slinton 6309679Slinton /* 631*16928Ssam * Return whether we are currently reading from standard input. 6329679Slinton */ 633*16928Ssam public Boolean isstdin() 634*16928Ssam { 6359679Slinton 636*16928Ssam return ((Boolean)(sp->s_type == ST_FILE && sp->s_file == stdin)); 637*16928Ssam } 638*16928Ssam 639*16928Ssam public Boolean istty() 6409679Slinton { 641*16928Ssam 642*16928Ssam return ((Boolean)isterm(sp->s_file)); 6439679Slinton } 644