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