xref: /openbsd-src/usr.bin/m4/expr.c (revision 62a742911104f98b9185b2c6b6007d9b1c36396c)
1 /*	$OpenBSD: expr.c,v 1.4 1998/04/25 18:47:19 millert Exp $	*/
2 /*	$NetBSD: expr.c,v 1.7 1995/09/28 05:37:31 tls 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 #ifndef lint
41 #if 0
42 static char sccsid[] = "@(#)expr.c	8.2 (Berkeley) 4/29/95";
43 #else
44 static char rcsid[] = "$OpenBSD: expr.c,v 1.4 1998/04/25 18:47:19 millert Exp $";
45 #endif
46 #endif /* not lint */
47 
48 #include <sys/cdefs.h>
49 #include <stdio.h>
50 
51 /*
52  *      expression evaluator: performs a standard recursive
53  *      descent parse to evaluate any expression permissible
54  *      within the following grammar:
55  *
56  *      expr    :       query EOS
57  *      query   :       lor
58  *              |       lor "?" query ":" query
59  *      lor     :       land { "||" land }
60  *      land    :       not { "&&" not }
61  *	not	:	eqrel
62  *		|	'!' not
63  *      eqrel   :       shift { eqrelop shift }
64  *      shift   :       primary { shop primary }
65  *      primary :       term { addop term }
66  *      term    :       exp { mulop exp }
67  *	exp	:	unary { expop unary }
68  *      unary   :       factor
69  *              |       unop unary
70  *      factor  :       constant
71  *              |       "(" query ")"
72  *      constant:       num
73  *              |       "'" CHAR "'"
74  *      num     :       DIGIT
75  *              |       DIGIT num
76  *      shop    :       "<<"
77  *              |       ">>"
78  *      eqrel   :       "="
79  *              |       "=="
80  *              |       "!="
81  *      	|       "<"
82  *              |       ">"
83  *              |       "<="
84  *              |       ">="
85  *
86  *
87  *      This expression evaluator is lifted from a public-domain
88  *      C Pre-Processor included with the DECUS C Compiler distribution.
89  *      It is hacked somewhat to be suitable for m4.
90  *
91  *      Originally by:  Mike Lutz
92  *                      Bob Harper
93  */
94 
95 #define TRUE    1
96 #define FALSE   0
97 #define EOS     (char) 0
98 #define EQL     0
99 #define NEQ     1
100 #define LSS     2
101 #define LEQ     3
102 #define GTR     4
103 #define GEQ     5
104 #define OCTAL   8
105 #define DECIMAL 10
106 
107 static char *nxtch;		       /* Parser scan pointer */
108 
109 static int query __P((void));
110 static int lor __P((void));
111 static int land __P((void));
112 static int not __P((void));
113 static int eqrel __P((void));
114 static int shift __P((void));
115 static int primary __P((void));
116 static int term __P((void));
117 static int exp __P((void));
118 static int unary __P((void));
119 static int factor __P((void));
120 static int constant __P((void));
121 static int num __P((void));
122 static int geteqrel __P((void));
123 static int skipws __P((void));
124 static void experr __P((char *));
125 
126 /*
127  * For longjmp
128  */
129 #include <setjmp.h>
130 static jmp_buf expjump;
131 
132 /*
133  * macros:
134  *      ungetch - Put back the last character examined.
135  *      getch   - return the next character from expr string.
136  */
137 #define ungetch()       nxtch--
138 #define getch()         *nxtch++
139 
140 int
141 expr(expbuf)
142 char *expbuf;
143 {
144 	register int rval;
145 
146 	nxtch = expbuf;
147 	if (setjmp(expjump) != 0)
148 		return FALSE;
149 
150 	rval = query();
151 	if (skipws() == EOS)
152 		return rval;
153 
154 	printf("m4: ill-formed expression.\n");
155 	return FALSE;
156 }
157 
158 /*
159  * query : lor | lor '?' query ':' query
160  */
161 static int
162 query()
163 {
164 	register int bool, true_val, false_val;
165 
166 	bool = lor();
167 	if (skipws() != '?') {
168 		ungetch();
169 		return bool;
170 	}
171 
172 	true_val = query();
173 	if (skipws() != ':')
174 		experr("bad query");
175 
176 	false_val = query();
177 	return bool ? true_val : false_val;
178 }
179 
180 /*
181  * lor : land { '||' land }
182  */
183 static int
184 lor()
185 {
186 	register int c, vl, vr;
187 
188 	vl = land();
189 	while ((c = skipws()) == '|') {
190 		if (getch() != '|')
191 			ungetch();
192 		vr = land();
193 		vl = vl || vr;
194 	}
195 
196 	ungetch();
197 	return vl;
198 }
199 
200 /*
201  * land : not { '&&' not }
202  */
203 static int
204 land()
205 {
206 	register int c, vl, vr;
207 
208 	vl = not();
209 	while ((c = skipws()) == '&') {
210 		if (getch() != '&')
211 			ungetch();
212 		vr = not();
213 		vl = vl && vr;
214 	}
215 
216 	ungetch();
217 	return vl;
218 }
219 
220 /*
221  * not : eqrel | '!' not
222  */
223 static int
224 not()
225 {
226 	register int val, c;
227 
228 	if ((c = skipws()) == '!' && getch() != '=') {
229 		ungetch();
230 		val = not();
231 		return !val;
232 	}
233 
234 	if (c == '!')
235 		ungetch();
236 	ungetch();
237 	return eqrel();
238 }
239 
240 /*
241  * eqrel : shift { eqrelop shift }
242  */
243 static int
244 eqrel()
245 {
246 	register int vl, vr, eqrel;
247 
248 	vl = shift();
249 	while ((eqrel = geteqrel()) != -1) {
250 		vr = shift();
251 
252 		switch (eqrel) {
253 
254 		case EQL:
255 			vl = (vl == vr);
256 			break;
257 		case NEQ:
258 			vl = (vl != vr);
259 			break;
260 
261 		case LEQ:
262 			vl = (vl <= vr);
263 			break;
264 		case LSS:
265 			vl = (vl < vr);
266 			break;
267 		case GTR:
268 			vl = (vl > vr);
269 			break;
270 		case GEQ:
271 			vl = (vl >= vr);
272 			break;
273 		}
274 	}
275 	return vl;
276 }
277 
278 /*
279  * shift : primary { shop primary }
280  */
281 static int
282 shift()
283 {
284 	register int vl, vr, c;
285 
286 	vl = primary();
287 	while (((c = skipws()) == '<' || c == '>') && getch() == c) {
288 		vr = primary();
289 
290 		if (c == '<')
291 			vl <<= vr;
292 		else
293 			vl >>= vr;
294 	}
295 
296 	if (c == '<' || c == '>')
297 		ungetch();
298 	ungetch();
299 	return vl;
300 }
301 
302 /*
303  * primary : term { addop term }
304  */
305 static int
306 primary()
307 {
308 	register int c, vl, vr;
309 
310 	vl = term();
311 	while ((c = skipws()) == '+' || c == '-') {
312 		vr = term();
313 
314 		if (c == '+')
315 			vl += vr;
316 		else
317 			vl -= vr;
318 	}
319 
320 	ungetch();
321 	return vl;
322 }
323 
324 /*
325  * <term> := <exp> { <mulop> <exp> }
326  */
327 static int
328 term()
329 {
330 	register int c, vl, vr;
331 
332 	vl = exp();
333 	while ((c = skipws()) == '*' || c == '/' || c == '%') {
334 		vr = exp();
335 
336 		switch (c) {
337 		case '*':
338 			vl *= vr;
339 			break;
340 		case '/':
341 			vl /= vr;
342 			break;
343 		case '%':
344 			vl %= vr;
345 			break;
346 		}
347 	}
348 	ungetch();
349 	return vl;
350 }
351 
352 /*
353  * <term> := <unary> { <expop> <unary> }
354  */
355 static int
356 exp()
357 {
358 	register int c, vl, vr, n;
359 
360 	vl = unary();
361 	switch (c = skipws()) {
362 
363 	case '*':
364 		if (getch() != '*') {
365 			ungetch();
366 			break;
367 		}
368 
369 	case '^':
370 		vr = exp();
371 		n = 1;
372 		while (vr-- > 0)
373 			n *= vl;
374 		return n;
375 	}
376 
377 	ungetch();
378 	return vl;
379 }
380 
381 /*
382  * unary : factor | unop unary
383  */
384 static int
385 unary()
386 {
387 	register int val, c;
388 
389 	if ((c = skipws()) == '+' || c == '-' || c == '~') {
390 		val = unary();
391 
392 		switch (c) {
393 		case '+':
394 			return val;
395 		case '-':
396 			return -val;
397 		case '~':
398 			return ~val;
399 		}
400 	}
401 
402 	ungetch();
403 	return factor();
404 }
405 
406 /*
407  * factor : constant | '(' query ')'
408  */
409 static int
410 factor()
411 {
412 	register int val;
413 
414 	if (skipws() == '(') {
415 		val = query();
416 		if (skipws() != ')')
417 			experr("bad factor");
418 		return val;
419 	}
420 
421 	ungetch();
422 	return constant();
423 }
424 
425 /*
426  * constant: num | 'char'
427  * Note: constant() handles multi-byte constants
428  */
429 static int
430 constant()
431 {
432 	register int i;
433 	register int value;
434 	register int c;
435 	int v[sizeof(int)];
436 
437 	if (skipws() != '\'') {
438 		ungetch();
439 		return num();
440 	}
441 	for (i = 0; i < sizeof(int); i++) {
442 		if ((c = getch()) == '\'') {
443 			ungetch();
444 			break;
445 		}
446 		if (c == '\\') {
447 			switch (c = getch()) {
448 			case '0':
449 			case '1':
450 			case '2':
451 			case '3':
452 			case '4':
453 			case '5':
454 			case '6':
455 			case '7':
456 				ungetch();
457 				c = num();
458 				break;
459 			case 'n':
460 				c = 012;
461 				break;
462 			case 'r':
463 				c = 015;
464 				break;
465 			case 't':
466 				c = 011;
467 				break;
468 			case 'b':
469 				c = 010;
470 				break;
471 			case 'f':
472 				c = 014;
473 				break;
474 			}
475 		}
476 		v[i] = c;
477 	}
478 	if (i == 0 || getch() != '\'')
479 		experr("illegal character constant");
480 	for (value = 0; --i >= 0;) {
481 		value <<= 8;
482 		value += v[i];
483 	}
484 	return value;
485 }
486 
487 /*
488  * num : digit | num digit
489  */
490 static int
491 num()
492 {
493 	register int rval, c, base;
494 	int ndig;
495 
496 	base = ((c = skipws()) == '0') ? OCTAL : DECIMAL;
497 	rval = 0;
498 	ndig = 0;
499 	while (c >= '0' && c <= (base == OCTAL ? '7' : '9')) {
500 		rval *= base;
501 		rval += (c - '0');
502 		c = getch();
503 		ndig++;
504 	}
505 	ungetch();
506 
507 	if (ndig == 0)
508 		experr("bad constant");
509 
510 	return rval;
511 
512 }
513 
514 /*
515  * eqrel : '=' | '==' | '!=' | '<' | '>' | '<=' | '>='
516  */
517 static int
518 geteqrel()
519 {
520 	register int c1, c2;
521 
522 	c1 = skipws();
523 	c2 = getch();
524 
525 	switch (c1) {
526 
527 	case '=':
528 		if (c2 != '=')
529 			ungetch();
530 		return EQL;
531 
532 	case '!':
533 		if (c2 == '=')
534 			return NEQ;
535 		ungetch();
536 		ungetch();
537 		return -1;
538 
539 	case '<':
540 		if (c2 == '=')
541 			return LEQ;
542 		ungetch();
543 		return LSS;
544 
545 	case '>':
546 		if (c2 == '=')
547 			return GEQ;
548 		ungetch();
549 		return GTR;
550 
551 	default:
552 		ungetch();
553 		ungetch();
554 		return -1;
555 	}
556 }
557 
558 /*
559  * Skip over any white space and return terminating char.
560  */
561 static int
562 skipws()
563 {
564 	register int c;
565 
566 	while ((c = getch()) <= ' ' && c > EOS)
567 		;
568 	return c;
569 }
570 
571 /*
572  * resets environment to eval(), prints an error
573  * and forces eval to return FALSE.
574  */
575 static void
576 experr(msg)
577 char *msg;
578 {
579 	printf("m4: %s in expr.\n", msg);
580 	longjmp(expjump, -1);
581 }
582