xref: /original-bsd/old/dbx/scanner.c (revision acfb0788)
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