1 /* Copyright (c) 1982 Regents of the University of California */ 2 3 static char sccsid[] = "@(#)scanner.c 1.11 (Berkeley) 08/17/84"; 4 5 /* 6 * Debugger scanner. 7 */ 8 9 #include <ctype.h> 10 #include "defs.h" 11 #include "scanner.h" 12 #include "main.h" 13 #include "keywords.h" 14 #include "tree.h" 15 #include "symbols.h" 16 #include "names.h" 17 #include "y.tab.h" 18 19 #ifndef public 20 typedef int Token; 21 #endif 22 23 typedef struct { 24 int s_type; 25 #define ST_FILE 0 26 #define ST_ALIAS 1 27 char *s_name; 28 int s_lineno; 29 union { 30 File su_file; 31 struct sum { 32 char *sum_data; 33 char *sum_cur; 34 } su_macro; 35 } su; 36 #define s_file su.su_file 37 #define s_macro su.su_macro 38 #define s_data s_macro.sum_data 39 #define s_cur s_macro.sum_cur 40 } STREAM; 41 42 #define NSTREAMS 10 43 private STREAM stack[NSTREAMS]; 44 private STREAM *sp = &stack[-1]; 45 46 public String initfile = ".dbxinit"; 47 48 private Token getident(); 49 private Token getnum(); 50 private Token getstring(); 51 private Char charcon(); 52 53 #define MAXLINESIZE 1024 54 private Char yytext[MAXLINESIZE]; 55 private Boolean shellmode; 56 private Boolean doaliases; 57 58 public scanner_init() 59 { 60 register Integer i; 61 62 if (sp < stack) 63 (void) pushinput(ST_FILE, nil, stdin); 64 shellmode = false; 65 doaliases = true; 66 errfilename = nil; 67 errlineno = sp->s_lineno = 0; 68 yytext[0] = '\0'; 69 } 70 71 #define MAXDEPTH 25 72 /* 73 * Read a single token. 74 * There are two "modes" of operation: one as in a compiler, 75 * and one for reading shell-like syntax. 76 */ 77 public Token yylex() 78 { 79 register int c; 80 register char *p; 81 register Token t; 82 static int depth = 0; 83 84 depth++; 85 if (depth > MAXDEPTH) { 86 depth = 0; 87 error("alias loop (maximum %d deep)", MAXDEPTH); 88 } 89 again: 90 do 91 c = getch(); 92 while (c == ' ' || c == '\t'); 93 if (isalpha(c) || c == '_' || c == '$') { 94 t = getident(c); 95 if (t == NAME && doaliases) { 96 p = findalias(yylval.y_name); 97 if (p != nil) { 98 if (lexdebug) 99 fprintf(stderr, "alias %s to \"%s\"\n", 100 ident(yylval.y_name), p); 101 if (!pushinput(ST_ALIAS, "", p)) { 102 unwindinput(ST_ALIAS); 103 error("Alias stack overflow."); 104 } 105 t = yylex(); 106 } 107 } 108 goto done; 109 } 110 if (isdigit(c)) { 111 t = shellmode ? getident(c) : getnum(c); 112 goto done; 113 } 114 switch (c) { 115 116 case '\n': 117 t = '\n'; 118 if (sp->s_lineno != 0) { 119 sp->s_lineno++; 120 if (sp->s_type == ST_FILE) 121 errlineno = sp->s_lineno; 122 } 123 break; 124 125 case '"': 126 case '\'': 127 t = getstring(c); 128 break; 129 130 case '.': 131 if (shellmode) { 132 t = getident(c); 133 break; 134 } 135 c = getch(); 136 ungetch(c); 137 t = isdigit(c) ? getnum('.') : '.'; 138 break; 139 140 case '<': 141 c = getch(); 142 if (shellmode || c != '<') { 143 ungetch(c); 144 t = '<'; 145 } else 146 t = LFORMER; 147 break; 148 149 case '>': 150 c = getch(); 151 if (shellmode || c != '>') { 152 ungetch(c); 153 t = '>'; 154 } else 155 t = RFORMER; 156 break; 157 158 case '#': 159 c = getch(); 160 if (c != '^') { 161 ungetch(c); 162 t = '#'; 163 } else 164 t = ABSTRACTION; 165 break; 166 167 case '-': 168 if (shellmode) { 169 t = getident(c); 170 break; 171 } 172 c = getch(); 173 if (c != '>') { 174 ungetch(c); 175 t = '-'; 176 } else 177 t = ARROW; 178 break; 179 180 case EOF: 181 t = 0; 182 break; 183 184 default: 185 t = shellmode && index("!&*()[];", c) == nil ? 186 getident(c) : c; 187 break; 188 } 189 done: 190 if (lexdebug) { 191 fprintf(stderr, "token "); 192 print_token(stderr, t); 193 fprintf(stderr, "\n"); 194 } 195 depth--; 196 return (t); 197 } 198 199 /* 200 * Scan an identifier and check to see if it's a keyword. 201 */ 202 private Token getident(c) 203 Char c; 204 { 205 register Char *p, *q; 206 Token t; 207 208 q = yytext; 209 if (shellmode) { 210 do { 211 *q++ = c; 212 c = getch(); 213 } while (index(" \t\n!&<>*[]();", c) == nil); 214 } else { 215 do { 216 *q++ = c; 217 c = getch(); 218 } while (isalnum(c) || c == '_' || c == '$'); 219 } 220 ungetch(c); 221 *q = '\0'; 222 yylval.y_name = identname(yytext, false); 223 if (shellmode) 224 return (NAME); 225 t = findkeyword(yylval.y_name); 226 return (t == nil ? NAME : t); 227 } 228 229 /* 230 * Scan a number. 231 */ 232 private Token getnum(c) 233 Char c; 234 { 235 register Char *q; 236 register Token t; 237 Integer base = 10; 238 239 q = yytext; 240 if (c == '0') { 241 c = getch(); 242 if (c == 'x') { 243 base = 16; 244 } else { 245 base = 8; 246 ungetch(c); 247 c = '0'; 248 } 249 } 250 if (base == 16) { 251 while (isdigit(c = getch()) || 252 (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F')) 253 *q++ = c; 254 } else { 255 do { 256 *q++ = c; 257 c = getch(); 258 } while (isdigit(c)); 259 } 260 if (c == '.') { 261 do { 262 *q++ = c; 263 c = getch(); 264 } while (isdigit(c)); 265 if (c == 'e' || c == 'E') { 266 c = getch(); 267 if (c == '+' || c == '-' || isdigit(c)) { 268 *q++ = 'e'; 269 do { 270 *q++ = c; 271 c = getch(); 272 } while (isdigit(c)); 273 } 274 } 275 ungetch(c); 276 *q = '\0'; 277 yylval.y_real = atof(yytext); 278 return (REAL); 279 } 280 ungetch(c); 281 *q = '\0'; 282 switch (base) { 283 284 case 10: 285 yylval.y_int = atol(yytext); 286 break; 287 288 case 8: 289 yylval.y_int = octal(yytext); 290 break; 291 292 case 16: 293 yylval.y_int = hex(yytext); 294 break; 295 296 default: 297 badcaseval(base); 298 } 299 return (INT); 300 } 301 302 /* 303 * Convert a string of octal digits to an integer. 304 */ 305 private int octal(s) 306 String s; 307 { 308 register Char *p; 309 register Integer n; 310 311 n = 0; 312 for (p = s; *p != '\0'; p++) 313 n = (n << 3) + (*p - '0'); 314 return (n); 315 } 316 317 /* 318 * Convert a string of hexadecimal digits to an integer. 319 */ 320 private int hex(s) 321 String s; 322 { 323 register Char *p; 324 register Integer n; 325 326 n = 0; 327 for (p = s; *p != '\0'; p++) { 328 n <<= 4; 329 if (*p >= 'a' && *p <= 'f') 330 n += (*p - 'a' + 10); 331 else if (*p >= 'A' && *p <= 'F') 332 n += (*p - 'A' + 10); 333 else 334 n += (*p - '0'); 335 } 336 return (n); 337 } 338 339 /* 340 * Scan a string. 341 */ 342 private Token getstring(match) 343 Char match; 344 { 345 register Char *q, c; 346 347 q = yytext; 348 for (;;) { 349 c = getch(); 350 if (c == '\n' || c == EOF) { 351 error("Unterminated string."); 352 break; 353 } 354 if (c == match) 355 break; 356 *q++ = charcon(c); 357 } 358 *q = '\0'; 359 yylval.y_string = strdup(yytext); 360 return (STRING); 361 } 362 363 /* 364 * Process a character constant. 365 * Watch out for backslashes. 366 */ 367 private Char charcon(c) 368 Char c; 369 { 370 register char *cp; 371 372 if (c == '\\') { 373 c = getch(); 374 if (isdigit(c)) { 375 int v; 376 377 v = 0; 378 do { 379 v = (v << 3) + (c - '0'); 380 c = getch(); 381 } while (isdigit(c)); 382 ungetch(c); 383 return (v); 384 } 385 for (cp = "f\ft\tb\bn\nr\rv\v"; *cp != c; cp += 2) 386 ; 387 if (*cp != '\0') 388 c = *cp; 389 } 390 return (c); 391 } 392 393 /* 394 * Parser error handling. 395 */ 396 public yyerror(s) 397 String s; 398 { 399 400 if (streq(s, "syntax error")) { 401 beginerrmsg(); 402 fprintf(stderr, "Syntax error"); 403 if (yytext[0] != '\0') 404 fprintf(stderr, " on \"%s\".", yytext); 405 enderrmsg(); 406 return; 407 } 408 error(s); 409 } 410 411 /* 412 * Eat the current line. 413 */ 414 private Char lastc = '\0'; 415 416 public gobble() 417 { 418 register char c; 419 420 if (lastc != '\n' && lastc != EOF) 421 while ((c = getch()) != EOF && c != '\n') 422 ; 423 } 424 425 /* 426 * Input file management routines. 427 */ 428 public setinput(filename) 429 Filename filename; 430 { 431 File f; 432 433 f = fopen(filename, "r"); 434 if (f == nil) 435 error("%s: Can't open.", filename); 436 if (!pushinput(ST_FILE, filename, f)) { 437 unwindinput(ST_FILE); 438 error("Source file nesting too deep."); 439 } 440 } 441 442 /* 443 * Send the current line to the shell. 444 */ 445 public shellline() 446 { 447 register Char *p, c; 448 449 for (p = yytext; (c = getch()) != EOF && c != '\n'; *p++ = c) 450 ; 451 *p = '\0'; 452 shell(yytext); 453 erecover(); 454 } 455 456 /* 457 * Read the rest of the current line in "shell mode". 458 */ 459 public beginshellmode() 460 { 461 462 shellmode = true; 463 } 464 465 public endshellmode() 466 { 467 468 shellmode = false; 469 } 470 471 public stopaliasing() 472 { 473 474 doaliases = false; 475 } 476 477 public startaliasing() 478 { 479 480 doaliases = true; 481 } 482 483 /* 484 * Print out a token for debugging. 485 */ 486 public print_token(f, t) 487 File f; 488 Token t; 489 { 490 491 switch (t) { 492 493 case '\n': 494 fprintf(f, "char '\\n'"); 495 return; 496 497 case EOF: 498 fprintf(f, "EOF"); 499 return; 500 501 case NAME: 502 case STRING: 503 fprintf(f, "%s, \"%s\"", keywdstring(t), ident(yylval.y_name)); 504 return; 505 } 506 if (t < 256) 507 fprintf(f, "char '%c'", t); 508 else 509 fprintf(f, "%s", keywdstring(t)); 510 } 511 512 public int getch() 513 { 514 int c; 515 516 again: 517 switch (sp->s_type) { 518 519 case ST_FILE: 520 c = getc(sp->s_file); 521 if (c == EOF && isterm(sp->s_file)) { 522 clearerr(sp->s_file); 523 putchar('\n'); 524 c = '\n'; 525 } 526 break; 527 528 case ST_ALIAS: 529 c = *sp->s_cur++; 530 if (c == '\0') { 531 c = EOF; 532 --sp->s_cur; 533 } 534 break; 535 536 default: 537 panic("Invalid input stream (type %d) to getch.", 538 sp->s_type); 539 } 540 if (c == EOF && popinput()) 541 goto again; 542 return (lastc = c); 543 } 544 545 private int ungetch(c) 546 Char c; 547 { 548 Char uc; 549 550 if (c != EOF) switch (sp->s_type) { 551 552 case ST_FILE: 553 uc = ungetc(c, sp->s_file); 554 break; 555 556 case ST_ALIAS: 557 if (sp->s_cur == sp->s_data) 558 panic("Illegal ungetch on alias."); 559 *--sp->s_cur = c; 560 uc = c; 561 break; 562 563 default: 564 panic("Invalid input stream (type %d) to ungetch.", 565 sp->s_type); 566 } 567 lastc = '\0'; 568 return (uc); 569 } 570 571 /* 572 * Push the current input stream and 573 * make the supplied stream the current. 574 */ 575 /*VARARGS3*/ 576 public pushinput(type, name, info) 577 int type; 578 Filename name; 579 { 580 581 if (sp >= &stack[NSTREAMS]) 582 return (0); 583 ++sp; 584 sp->s_type = type; 585 switch (type) { 586 587 case ST_FILE: 588 sp->s_file = (File)info; 589 errfilename = sp->s_name = name; 590 errlineno = sp->s_lineno = 1; 591 break; 592 593 case ST_ALIAS: 594 sp->s_cur = sp->s_data = (char *)info; 595 break; 596 597 default: 598 panic("Invalid input stream (type %d) to pushinput.", type); 599 } 600 return (1); 601 } 602 603 public popinput() 604 { 605 606 if (sp <= &stack[0]) /* never pop stdin or equivalent */ 607 return (0); 608 if (sp->s_type == ST_FILE && sp->s_file != stdin) 609 fclose(sp->s_file); 610 --sp; 611 if (sp->s_type == ST_FILE) 612 errfilename = sp->s_name; 613 errlineno = sp->s_lineno; 614 return (1); 615 } 616 617 /* 618 * Unwind the input stack of all input types specified. 619 * This is called to recover from an infinite 620 * loop in alias processing or source file including. 621 */ 622 public unwindinput(type) 623 Integer type; 624 { 625 626 while (sp->s_type == type && popinput()) 627 ; 628 } 629 630 /* 631 * Return whether we are currently reading from standard input. 632 */ 633 public Boolean isstdin() 634 { 635 636 return ((Boolean)(sp->s_type == ST_FILE && sp->s_file == stdin)); 637 } 638 639 public Boolean istty() 640 { 641 642 return ((Boolean)isterm(sp->s_file)); 643 } 644