xref: /original-bsd/old/adb/common_source/expr.c (revision dfa70498)
1 #ifndef lint
2 static char sccsid[] = "@(#)expr.c	5.1 (Berkeley) 01/16/89";
3 #endif
4 
5 /*
6  * adb - expression parser
7  */
8 
9 #include "defs.h"
10 #include <ctype.h>
11 
12 extern char BADSYM[];		/* "symbol not found" */
13 extern char BADVAR[];		/* "bad variable" */
14 extern char BADSYN[];		/* "syntax error" */
15 extern char NOCFN[];		/* "c routine not found" */
16 extern char NOADR[];		/* "address expected" */
17 extern char BADLOC[];		/* "automatic variable not found" */
18 extern char NOPCS[];		/* "no process" */
19 
20 struct	nlist *xxxsym;		/* last symbol found due to expression */
21 	/* change this name back to cursym AFTER testing!... */
22 struct	activation curframe;	/* current stack frame (for local vars) */
23 
24 /*
25  * This file implements a small recursive descent expression parser.
26  * The syntax is (in YACC terms):
27  *
28  *	expr	: expr1
29  *		|		(empty)
30  *		;
31  *
32  *	expr1	: term
33  *		| term dyadic expr1
34  *		;
35  *
36  *	dyadic	: '+'		(addition)
37  *		| '-'		(subtraction)
38  *		| '#'		(roundup)
39  *		| '*'		(multiplication)
40  *		| '%'		(division)
41  *		| '&'		(bitwise and)
42  *		| '|'		(bitwise or)
43  *		;
44  *
45  *	term	: item
46  *		| monadic term
47  *		| '(' expr ')'
48  *		;
49  *
50  *	monadic	: '*'		(contents of core, or SP_DATA)
51  *		| '@'		(contents of a.out, or SP_INSTR)
52  *		| '-'		(negation)
53  *		| '~'		(bitwise not)
54  *		| '#'		(logical not)
55  *		;
56  *
57  *	item	: number	(current radix; 0o,0t,0x; or float)
58  *		| name		(value from symbol table)
59  *		| rtn '.' name	(address of name in routine rtn)
60  *		| rtn '.'	(???)
61  *		| '.' name	(???)
62  *		| '.'		(value of dot)
63  *		| '+'		(dot + current increment)
64  *		| '^'		(dot - current increment)
65  *		| '"'		(last address typed)
66  *		| '<' var	(value of variable var)
67  *		| '<' register	(value in register)
68  *		| '\'' ch '\''	(character(s))
69  *		;
70  *
71  * The empty string handling is actually done in `item', but callers
72  * can simply assume that expr() returns 1 if it finds an expression,
73  * or 0 if not, and that rexpr() errors out if there is no expression.
74  *
75  * The routines symchar() and getsym() handle `name's and `rtn's.
76  * The routine getnum(), with helper getfloat(), handles `number's.
77  */
78 
79 /* flags for symchar() */
80 #define	SYMCH_READ	1	/* call readchar() first */
81 #define	SYMCH_DIGITS	2	/* allow digits */
82 
83 /*
84  * Return true if the next (how & SYMCH_READ) or current character
85  * is a symbol character; allow digits if (how & SYMCH_DIGITS).
86  */
87 static int
88 symchar(how)
89 	int how;
90 {
91 
92 	if (how & SYMCH_READ)
93 		(void) readchar();
94 	if (lastc == '\\') {
95 		(void) readchar();
96 		return (1);
97 	}
98 	if (isalpha(lastc) || lastc == '_')
99 		return (1);
100 	return ((how & SYMCH_DIGITS) && isdigit(lastc));
101 }
102 
103 /*
104  * Read a symbol into the given buffer.  The first character is
105  * assumed already to have been read.
106  */
107 static
108 getsym(symbuf, symlen)
109 	register char *symbuf;
110 	register int symlen;
111 {
112 
113 	do {
114 		if (--symlen > 0)
115 			*symbuf++ = lastc;
116 	} while (symchar(SYMCH_READ | SYMCH_DIGITS));
117 	*symbuf = 0;
118 }
119 
120 /*
121  * Read a number.  The converted value is stored in expv.
122  * The caller has already determined that there is at least one digit.
123  */
124 static
125 getnum()
126 {
127 	register int base, c;
128 
129 	expv = 0;
130 	if ((base = radix) < 0)
131 		base = -base;
132 	if (lastc == '0') {
133 		switch (readchar()) {
134 		case 'x': case 'X':
135 			base = 16;
136 			(void) readchar();
137 			break;
138 		case 't': case 'T':
139 			base = 10;
140 			(void) readchar();
141 			break;
142 		case 'o': case 'O':
143 			base = 8;
144 			(void) readchar();
145 		}
146 	}
147 	for (c = lastc; isxdigit(c); c = readchar()) {
148 		if (isdigit(c))
149 			c -= '0';
150 		else if (base <= 10)
151 			break;
152 		else
153 			c -= isupper(c) ? 'A' - 10 : 'a' - 10;
154 		if (c >= base)
155 			error(BADSYN);
156 		/* since expv is unsigned, the following cannot overflow */
157 		expv = expv * base + c;
158 	}
159 	if (lastc == '.' && (base == 10 || expv == 0))
160 		getfloat();
161 	unreadc();
162 }
163 
164 /*
165  * Read a float.  The integer part is already in expv.  Set expv
166  * to the integer bit pattern that corresponds to the float.
167  *
168  * The following routine could be improved, but at least it will
169  * not crash on input such as 0.999999999999999999999999999999,
170  * as did the original.
171  */
172 getfloat()
173 {
174 	register int i;
175 	register char *p;
176  /* THE FOLLOWING ASSUMES sizeof(float)==sizeof(expr_t) */
177  /* PERHAPS THIS SHOULD BE MOVED TO MACHINE DEPENDENT CODE */
178 	union {
179 		float r;
180 		expr_t e;
181 	} gross;
182  /* end machine dependent */
183 	char hackbuf[50];
184 	double atof();
185 
186 	for (i = sizeof(hackbuf), p = hackbuf; isdigit(readchar());)
187 		if (--i > 0)
188 			*p++ = lastc;
189 	*p = 0;
190 	gross.r = expv + atof(hackbuf);
191 	expv = gross.e;
192 }
193 
194 /*
195  * item : number | name [ '.' local ] | '.' local | '.' | '+' | '^' | '"' |
196  *	  '<' var | '<' register | '\'' char(s) '\'' ;
197  *
198  * item returns 1 if it finds an item, or 0 if it resolves to
199  * the empty string.
200  */
201 static int
202 item(allownil)
203 	int allownil;
204 {
205 	register int i, c;
206 	struct reglist *reg;
207 
208 	c = readchar();
209 	if (isdigit(c)) {
210 		getnum();
211 		return (1);
212 	}
213 	if (symchar(0)) {
214 		ev_name();
215 		return (1);
216 	}
217 	switch (c) {
218 
219 	case '.':
220 		if (symchar(SYMCH_READ))
221 			ev_local();	/* SHOULD RESET xxxsym FIRST? */
222 		else
223 			expv = dot;
224 		unreadc();
225 		return (1);
226 
227 	case '"':
228 		expv = ditto;
229 		return (1);
230 
231 	case '+':
232 		expv = inkdot(dotinc);
233 		return (1);
234 
235 	case '^':
236 		expv = inkdot(-dotinc);
237 		return (1);
238 
239 	case '<':
240 		if ((reg = reglookup()) != NULL) {
241 			expv = getreg(reg);
242 			return (1);
243 		}
244 		else if ((i = varlookup(rdc())) != -1)
245 			expv = var[i];
246 		else
247 			error(BADVAR);
248 		return (1);
249 
250 	case '\'':
251 		i = sizeof(expr_t) / sizeof(char);
252 		for (expv = 0;; expv = (expv << NBBY) | c) {
253 			if ((c = readchar()) == '\\') {
254 				if ((c = readchar()) == 0)
255 					break;
256 			} else if (c == '\'')
257 				break;
258 			if (--i < 0)
259 				error(BADSYN);
260 		}
261 		return (1);
262 	}
263 	if (!allownil)
264 		error(NOADR);
265 	unreadc();
266 	return (0);
267 }
268 
269 /*
270  * term : item | monadic_op term | '(' expr ')' ;
271  */
272 term(allownil)
273 	int allownil;
274 {
275 
276 	switch (readchar()) {
277 
278 	case '*':
279 	case '@':
280 		(void) term(0);
281 		(void) adbread(lastc == '@' ? SP_INSTR : SP_DATA,
282 		    (addr_t)expv, (caddr_t)&expv, sizeof(expv));
283 		checkerr();
284 		return (1);
285 
286 	case '-':
287 		(void) term(0);
288 		expv = -expv;
289 		return (1);
290 
291 	case '~':
292 		(void) term(0);
293 		expv = ~expv;
294 		return (1);
295 
296 	case '#':
297 		(void) term(0);
298 		expv = !expv;
299 		return (1);
300 
301 	case '(':
302 		(void) iexpr(0);
303 		if (readchar() != ')')
304 			error(BADSYN);
305 		return (1);
306 
307 	default:
308 		unreadc();
309 		return (item(allownil));
310 	}
311 }
312 
313 /*
314  * expr : term | term dyadic expr | ;
315  * (internal version, which passes on the allow-nil flag)
316  */
317 static int
318 iexpr(allownil)
319 	int allownil;
320 {
321 	register expr_t lhs, t;
322 
323 	(void) rdc();
324 	unreadc();
325 	if (!term(allownil))
326 		return (0);
327 	for (;;) {
328 		lhs = expv;
329 		switch (readchar()) {
330 
331 		case '+':
332 			(void) term(0);
333 			expv += lhs;
334 			break;
335 
336 		case '-':
337 			(void) term(0);
338 			expv = lhs - expv;
339 			break;
340 
341 		case '#':
342 			(void) term(0);
343 			if (expv == 0)
344 				error("# by 0");
345 			/* roundup(lhs, expv), but careful about overflow */
346 			t = lhs / expv;
347 			t *= expv;
348 			expv = t == lhs ? t : t + expv;
349 			break;
350 
351 		case '*':
352 			(void) term(0);
353 			expv *= lhs;
354 			break;
355 
356 		case '%':
357 			(void) term(0);
358 			expv = lhs / expv;
359 			break;
360 
361 		case '&':
362 			(void) term(0);
363 			expv &= lhs;
364 			break;
365 
366 		case '|':
367 			(void) term(0);
368 			expv |= lhs;
369 			break;
370 
371 		default:
372 			unreadc();
373 			return (1);
374 		}
375 	}
376 }
377 
378 int
379 oexpr()
380 {
381 
382 	return (iexpr(1));
383 }
384 
385 expr_t
386 rexpr()
387 {
388 
389 	(void) iexpr(0);
390 	return (expv);
391 }
392 
393 /*
394  * Evaluate a name, or a name '.' localname.
395  */
396 static
397 ev_name()
398 {
399 	struct nlist *symp;
400 	char symbuf[SYMLEN];
401 
402 	/* name [ . localname ] */
403 	getsym(symbuf, sizeof(symbuf));
404 	if (lastc == '.')	/* name . local */
405 		find_frame(symbuf);
406 	else if ((symp = lookup(symbuf)) != NULL)
407 		expv = (xxxsym = symp)->n_value;
408 	else
409 		error(BADSYM);
410 	unreadc();
411 }
412 
413 /*
414  * Backtrack through the call stack to find the symbol in symbuf.
415  * Save the result, and if there is another name, look for it within
416  * that frame.  Otherwise the value of the expression is the address
417  * of the found frame.
418  */
419 static
420 find_frame(symbuf)
421 	char *symbuf;
422 {
423 	struct activation a;
424 	addr_t dummy;		/* for findsym() to scribble on */
425 
426 	if (pid == 0)
427 		error(NOPCS);
428 	for (a_init(&a); a.a_valid; a_back(&a)) {
429 		checkerr();
430 		if ((xxxsym = findsym(a.a_pc, SP_INSTR, &dummy)) == NULL)
431 			break;
432 		if (eqsym(xxxsym->n_un.n_name, symbuf, '_')) {
433 			curframe = a;
434 			if (symchar(SYMCH_READ))
435 				ev_local();
436 			else
437 				expv = a.a_fp;
438 			return;
439 		}
440 	}
441 	error(NOCFN);
442 	/* NOTREACHED */
443 }
444 
445 /*
446  * Linear search (ugh) for a symbol in the current stack frame.
447  */
448 static
449 ev_local()
450 {
451 	register struct nlist *sp;
452 	register char *a, *b;
453 	char symbuf[SYMLEN];
454 
455 	if (pid == 0)
456 		error(NOPCS);
457 	if (!curframe.a_valid || (sp = xxxsym) == NULL)
458 		error(NOCFN);
459 	getsym(symbuf, SYMLEN);
460 	while ((sp = nextlocal(sp)) != NULL) {
461 		/*
462 		 * Local and parameter symbols (as generated by .stabs)
463 		 * end with ':', not '\0'; here we allow both.
464 		 */
465 		if (*(a = sp->n_un.n_name) != *(b = symbuf))
466 			continue;
467 		while (*a == *b++)
468 			if (*a++ == 0 || *a == ':') {
469 				expv = eval_localsym(sp, &curframe);
470 				xxxsym = sp;	/* ??? */
471 				return;
472 			}
473 	}
474 	error(BADLOC);
475 }
476 
477 #ifndef inkdot
478 /*
479  * Function version of inkdot().  Compute the new dot, and check for
480  * address wrap-around.
481  */
482 addr_t
483 inkdot(incr)
484 	int incr;
485 {
486 	addr_t newdot = dot + incr;
487 
488 	if (ADDRESS_WRAP(dot, newdot))
489 		error(ADWRAP);
490 	return (newdot);
491 }
492 #endif
493