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