xref: /netbsd-src/usr.bin/m4/expr.c (revision 1ca5c1b28139779176bd5c13ad7c5f25c0bcd5f8)
1 /*	$NetBSD: expr.c,v 1.12 2001/11/14 06:16:08 tv Exp $	*/
2 /*	$OpenBSD: expr.c,v 1.11 2000/01/11 14:00:57 espie Exp $	*/
3 
4 /*
5  * Copyright (c) 1989, 1993
6  *	The Regents of the University of California.  All rights reserved.
7  *
8  * This code is derived from software contributed to Berkeley by
9  * Ozan Yigit at York University.
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions
13  * are met:
14  * 1. Redistributions of source code must retain the above copyright
15  *    notice, this list of conditions and the following disclaimer.
16  * 2. Redistributions in binary form must reproduce the above copyright
17  *    notice, this list of conditions and the following disclaimer in the
18  *    documentation and/or other materials provided with the distribution.
19  * 3. All advertising materials mentioning features or use of this software
20  *    must display the following acknowledgement:
21  *	This product includes software developed by the University of
22  *	California, Berkeley and its contributors.
23  * 4. Neither the name of the University nor the names of its contributors
24  *    may be used to endorse or promote products derived from this software
25  *    without specific prior written permission.
26  *
27  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
28  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
29  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
30  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
31  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
32  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
33  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
34  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
35  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
36  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
37  * SUCH DAMAGE.
38  */
39 
40 #include <sys/cdefs.h>
41 #ifndef lint
42 #if 0
43 static char sccsid[] = "@(#)expr.c	8.2 (Berkeley) 4/29/95";
44 #else
45 __RCSID("$NetBSD: expr.c,v 1.12 2001/11/14 06:16:08 tv Exp $");
46 #endif
47 #endif /* not lint */
48 
49 #include <sys/cdefs.h>
50 #include <ctype.h>
51 #include <err.h>
52 #include <stddef.h>
53 #include <stdio.h>
54 #include "mdef.h"
55 #include "extern.h"
56 
57 /*
58  *      expression evaluator: performs a standard recursive
59  *      descent parse to evaluate any expression permissible
60  *      within the following grammar:
61  *
62  *      expr    :       query EOS
63  *      query   :       lor
64  *              |       lor "?" query ":" query
65  *      lor     :       land { "||" land }
66  *      land    :       not { "&&" not }
67  *	not	:	eqrel
68  *		|	'!' not
69  *      eqrel   :       shift { eqrelop shift }
70  *      shift   :       primary { shop primary }
71  *      primary :       term { addop term }
72  *      term    :       exp { mulop exp }
73  *	exp	:	unary { expop unary }
74  *      unary   :       factor
75  *              |       unop unary
76  *      factor  :       constant
77  *              |       "(" query ")"
78  *      constant:       num
79  *              |       "'" CHAR "'"
80  *      num     :       DIGIT
81  *              |       DIGIT num
82  *      shop    :       "<<"
83  *              |       ">>"
84  *      eqrel   :       "="
85  *              |       "=="
86  *              |       "!="
87  *      	|       "<"
88  *              |       ">"
89  *              |       "<="
90  *              |       ">="
91  *
92  *
93  *      This expression evaluator is lifted from a public-domain
94  *      C Pre-Processor included with the DECUS C Compiler distribution.
95  *      It is hacked somewhat to be suitable for m4.
96  *
97  *      Originally by:  Mike Lutz
98  *                      Bob Harper
99  */
100 
101 #define EQL     0
102 #define NEQ     1
103 #define LSS     2
104 #define LEQ     3
105 #define GTR     4
106 #define GEQ     5
107 #define OCTAL   8
108 #define DECIMAL 10
109 #define HEX	16
110 
111 static const char *nxtch;		       /* Parser scan pointer */
112 static const char *where;
113 
114 static int query __P((void));
115 static int lor __P((void));
116 static int land __P((void));
117 static int not __P((void));
118 static int eqrel __P((void));
119 static int shift __P((void));
120 static int primary __P((void));
121 static int term __P((void));
122 static int exp __P((void));
123 static int unary __P((void));
124 static int factor __P((void));
125 static int constant __P((void));
126 static int num __P((void));
127 static int geteqrel __P((void));
128 static int skipws __P((void));
129 static void experr __P((const char *));
130 
131 /*
132  * For longjmp
133  */
134 #include <setjmp.h>
135 static jmp_buf expjump;
136 
137 /*
138  * macros:
139  *      ungetch - Put back the last character examined.
140  *      getch   - return the next character from expr string.
141  */
142 #define ungetch()       nxtch--
143 #define getch()         *nxtch++
144 
145 int
146 expr(expbuf)
147 	const char *expbuf;
148 {
149 	int rval;
150 
151 	nxtch = expbuf;
152 	where = expbuf;
153 	if (setjmp(expjump) != 0)
154 		return FALSE;
155 
156 	rval = query();
157 	if (skipws() == EOS)
158 		return rval;
159 
160 	printf("m4: ill-formed expression.\n");
161 	return FALSE;
162 }
163 
164 /*
165  * query : lor | lor '?' query ':' query
166  */
167 static int
168 query()
169 {
170 	int bool, true_val, false_val;
171 
172 	bool = lor();
173 	if (skipws() != '?') {
174 		ungetch();
175 		return bool;
176 	}
177 
178 	true_val = query();
179 	if (skipws() != ':')
180 		experr("bad query");
181 
182 	false_val = query();
183 	return bool ? true_val : false_val;
184 }
185 
186 /*
187  * lor : land { '||' land }
188  */
189 static int
190 lor()
191 {
192 	int c, vl, vr;
193 
194 	vl = land();
195 	while ((c = skipws()) == '|') {
196 		if (getch() != '|')
197 			ungetch();
198 		vr = land();
199 		vl = vl || vr;
200 	}
201 
202 	ungetch();
203 	return vl;
204 }
205 
206 /*
207  * land : not { '&&' not }
208  */
209 static int
210 land()
211 {
212 	int c, vl, vr;
213 
214 	vl = not();
215 	while ((c = skipws()) == '&') {
216 		if (getch() != '&')
217 			ungetch();
218 		vr = not();
219 		vl = vl && vr;
220 	}
221 
222 	ungetch();
223 	return vl;
224 }
225 
226 /*
227  * not : eqrel | '!' not
228  */
229 static int
230 not()
231 {
232 	int val, c;
233 
234 	if ((c = skipws()) == '!' && getch() != '=') {
235 		ungetch();
236 		val = not();
237 		return !val;
238 	}
239 
240 	if (c == '!')
241 		ungetch();
242 	ungetch();
243 	return eqrel();
244 }
245 
246 /*
247  * eqrel : shift { eqrelop shift }
248  */
249 static int
250 eqrel()
251 {
252 	int vl, vr, eqrelvar;
253 
254 	vl = shift();
255 	while ((eqrelvar = geteqrel()) != -1) {
256 		vr = shift();
257 
258 		switch (eqrelvar) {
259 
260 		case EQL:
261 			vl = (vl == vr);
262 			break;
263 		case NEQ:
264 			vl = (vl != vr);
265 			break;
266 
267 		case LEQ:
268 			vl = (vl <= vr);
269 			break;
270 		case LSS:
271 			vl = (vl < vr);
272 			break;
273 		case GTR:
274 			vl = (vl > vr);
275 			break;
276 		case GEQ:
277 			vl = (vl >= vr);
278 			break;
279 		}
280 	}
281 	return vl;
282 }
283 
284 /*
285  * shift : primary { shop primary }
286  */
287 static int
288 shift()
289 {
290 	int vl, vr, c;
291 
292 	vl = primary();
293 	while (((c = skipws()) == '<' || c == '>') && getch() == c) {
294 		vr = primary();
295 
296 		if (c == '<')
297 			vl <<= vr;
298 		else
299 			vl >>= vr;
300 	}
301 
302 	if (c == '<' || c == '>')
303 		ungetch();
304 	ungetch();
305 	return vl;
306 }
307 
308 /*
309  * primary : term { addop term }
310  */
311 static int
312 primary()
313 {
314 	int c, vl, vr;
315 
316 	vl = term();
317 	while ((c = skipws()) == '+' || c == '-') {
318 		vr = term();
319 
320 		if (c == '+')
321 			vl += vr;
322 		else
323 			vl -= vr;
324 	}
325 
326 	ungetch();
327 	return vl;
328 }
329 
330 /*
331  * <term> := <exp> { <mulop> <exp> }
332  */
333 static int
334 term()
335 {
336 	int c, vl, vr;
337 
338 	vl = exp();
339 	while ((c = skipws()) == '*' || c == '/' || c == '%') {
340 		vr = exp();
341 
342 		switch (c) {
343 		case '*':
344 			vl *= vr;
345 			break;
346 		case '/':
347 			if (vr == 0)
348 				errx(1, "division by zero in eval.");
349 			else
350 				vl /= vr;
351 			break;
352 		case '%':
353 			if (vr == 0)
354 				errx(1, "modulo zero in eval.");
355 			else
356 				vl %= vr;
357 			break;
358 		}
359 	}
360 	ungetch();
361 	return vl;
362 }
363 
364 /*
365  * <term> := <unary> { <expop> <unary> }
366  */
367 static int
368 exp()
369 {
370 	int c, vl, vr, n;
371 
372 	vl = unary();
373 	switch (c = skipws()) {
374 
375 	case '*':
376 		if (getch() != '*') {
377 			ungetch();
378 			break;
379 		}
380 
381 	case '^':
382 		vr = exp();
383 		n = 1;
384 		while (vr-- > 0)
385 			n *= vl;
386 		return n;
387 	}
388 
389 	ungetch();
390 	return vl;
391 }
392 
393 /*
394  * unary : factor | unop unary
395  */
396 static int
397 unary()
398 {
399 	int val, c;
400 
401 	if ((c = skipws()) == '+' || c == '-' || c == '~') {
402 		val = unary();
403 
404 		switch (c) {
405 		case '+':
406 			return val;
407 		case '-':
408 			return -val;
409 		case '~':
410 			return ~val;
411 		}
412 	}
413 
414 	ungetch();
415 	return factor();
416 }
417 
418 /*
419  * factor : constant | '(' query ')'
420  */
421 static int
422 factor()
423 {
424 	int val;
425 
426 	if (skipws() == '(') {
427 		val = query();
428 		if (skipws() != ')')
429 			experr("bad factor");
430 		return val;
431 	}
432 
433 	ungetch();
434 	return constant();
435 }
436 
437 /*
438  * constant: num | 'char'
439  * Note: constant() handles multi-byte constants
440  */
441 static int
442 constant()
443 {
444 	int i;
445 	int value;
446 	char c;
447 	int v[sizeof(int)];
448 
449 	if (skipws() != '\'') {
450 		ungetch();
451 		return num();
452 	}
453 	for (i = 0; i < sizeof(int); i++) {
454 		if ((c = getch()) == '\'') {
455 			ungetch();
456 			break;
457 		}
458 		if (c == '\\') {
459 			switch (c = getch()) {
460 			case '0':
461 			case '1':
462 			case '2':
463 			case '3':
464 			case '4':
465 			case '5':
466 			case '6':
467 			case '7':
468 				ungetch();
469 				c = num();
470 				break;
471 			case 'n':
472 				c = 012;
473 				break;
474 			case 'r':
475 				c = 015;
476 				break;
477 			case 't':
478 				c = 011;
479 				break;
480 			case 'b':
481 				c = 010;
482 				break;
483 			case 'f':
484 				c = 014;
485 				break;
486 			}
487 		}
488 		v[i] = c;
489 	}
490 	if (i == 0 || getch() != '\'')
491 		experr("illegal character constant");
492 	for (value = 0; --i >= 0;) {
493 		value <<= 8;
494 		value += v[i];
495 	}
496 	return value;
497 }
498 
499 /*
500  * num : digit | num digit
501  */
502 static int
503 num()
504 {
505 	int rval, c, base;
506 	int ndig;
507 
508 	base = ((c = skipws()) == '0') ? OCTAL : DECIMAL;
509 	rval = 0;
510 	ndig = 0;
511 	if (base == OCTAL) {
512 		c = skipws();
513 		if (c == 'x' || c == 'X') {
514 			base = HEX;
515 			c = skipws();
516 		} else
517 			ndig++;
518 	}
519 	while ((base == HEX && isxdigit(c)) ||
520 			(c >= '0' && c <= (base == OCTAL ? '7' : '9'))) {
521 		rval *= base;
522 		if (isalpha(c))
523 			rval += (tolower(c) - 'a' + 10);
524 		else
525 			rval += (c - '0');
526 		c = getch();
527 		ndig++;
528 	}
529 	ungetch();
530 
531 	if (ndig == 0)
532 		experr("bad constant");
533 
534 	return rval;
535 }
536 
537 /*
538  * eqrel : '=' | '==' | '!=' | '<' | '>' | '<=' | '>='
539  */
540 static int
541 geteqrel()
542 {
543 	int c1, c2;
544 
545 	c1 = skipws();
546 	c2 = getch();
547 
548 	switch (c1) {
549 
550 	case '=':
551 		if (c2 != '=')
552 			ungetch();
553 		return EQL;
554 
555 	case '!':
556 		if (c2 == '=')
557 			return NEQ;
558 		ungetch();
559 		ungetch();
560 		return -1;
561 
562 	case '<':
563 		if (c2 == '=')
564 			return LEQ;
565 		ungetch();
566 		return LSS;
567 
568 	case '>':
569 		if (c2 == '=')
570 			return GEQ;
571 		ungetch();
572 		return GTR;
573 
574 	default:
575 		ungetch();
576 		ungetch();
577 		return -1;
578 	}
579 }
580 
581 /*
582  * Skip over any white space and return terminating char.
583  */
584 static int
585 skipws()
586 {
587 	char c;
588 
589 	while ((c = getch()) <= ' ' && c > EOS)
590 		;
591 	return c;
592 }
593 
594 /*
595  * resets environment to eval(), prints an error
596  * and forces eval to return FALSE.
597  */
598 static void
599 experr(msg)
600 	const char *msg;
601 {
602 	printf("m4: %s in expr %s.\n", msg, where);
603 	longjmp(expjump, -1);
604 }
605