expr.c revision 226422
1/*	$OpenBSD: expr.c,v 1.14 2002/04/26 16:15:16 espie 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 * 4. Neither the name of the University nor the names of its contributors
20 *    may be used to endorse or promote products derived from this software
21 *    without specific prior written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 * SUCH DAMAGE.
34 */
35
36#ifndef lint
37#if 0
38static char sccsid[] = "@(#)expr.c	8.2 (Berkeley) 4/29/95";
39#else
40#if 0
41static char rcsid[] = "$OpenBSD: expr.c,v 1.14 2002/04/26 16:15:16 espie Exp $";
42#endif
43#endif
44#endif /* not lint */
45
46#include <sys/cdefs.h>
47__FBSDID("$FreeBSD: head/usr.bin/m4/expr.c 226422 2011-10-16 08:09:17Z ed $");
48
49#include <sys/types.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    :       bor { "&&" bor }
67 *      bor     :       xor { "|" xor }
68 *      xor     :       band { "^" eqrel }
69 *      band    :       eqrel { "&" eqrel }
70 *      eqrel   :       nerel { ("==" | "!=") nerel }
71 *      nerel   :       shift { ("<" | ">" | "<=" | ">=") shift }
72 *      shift   :       primary { ("<<" | ">>") primary }
73 *      primary :       term { ("+" | "-") term }
74 *      term    :       exponent { ("*" | "/" | "%") exponent }
75 *      exponent:       unary { "**" unary }
76 *      unary   :       factor
77 *              |       ("+" | "-" | "~" | "!") unary
78 *      factor  :       constant
79 *              |       "(" query ")"
80 *      constant:       num
81 *              |       "'" CHAR "'"
82 *      num     :       DIGIT
83 *              |       DIGIT num
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 EQL     0
95#define NEQ     1
96#define LSS     2
97#define LEQ     3
98#define GTR     4
99#define GEQ     5
100#define OCTAL   8
101#define DECIMAL 10
102#define HEX	16
103
104static const char *nxtch;		       /* Parser scan pointer */
105static const char *where;
106
107static int query(int mayeval);
108static int lor(int mayeval);
109static int land(int mayeval);
110static int bor(int mayeval);
111static int xor(int mayeval);
112static int band(int mayeval);
113static int eqrel(int mayeval);
114static int nerel(int mayeval);
115static int shift(int mayeval);
116static int primary(int mayeval);
117static int term(int mayeval);
118static int exponent(int mayeval);
119static int unary(int mayeval);
120static int factor(int mayeval);
121static int constant(int mayeval);
122static int num(int mayeval);
123static int skipws(void);
124static void experr(const char *);
125
126/*
127 * For longjmp
128 */
129#include <setjmp.h>
130static 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
140int
141expr(const char *expbuf)
142{
143	int rval;
144
145	nxtch = expbuf;
146	where = expbuf;
147	if (setjmp(expjump) != 0)
148		return FALSE;
149
150	rval = query(1);
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 */
161static int
162query(int mayeval)
163{
164	int result, true_val, false_val;
165
166	result = lor(mayeval);
167	if (skipws() != '?') {
168		ungetch();
169		return result;
170	}
171
172	true_val = query(result);
173	if (skipws() != ':')
174		experr("bad query: missing \":\"");
175
176	false_val = query(!result);
177	return result ? true_val : false_val;
178}
179
180/*
181 * lor : land { '||' land }
182 */
183static int
184lor(int mayeval)
185{
186	int c, vl, vr;
187
188	vl = land(mayeval);
189	while ((c = skipws()) == '|') {
190		if (getch() != '|') {
191			ungetch();
192			break;
193		}
194		if (vl != 0)
195			mayeval = 0;
196		vr = land(mayeval);
197		vl = vl || vr;
198	}
199
200	ungetch();
201	return vl;
202}
203
204/*
205 * land : not { '&&' not }
206 */
207static int
208land(int mayeval)
209{
210	int c, vl, vr;
211
212	vl = bor(mayeval);
213	while ((c = skipws()) == '&') {
214		if (getch() != '&') {
215			ungetch();
216			break;
217		}
218		if (vl == 0)
219			mayeval = 0;
220		vr = bor(mayeval);
221		vl = vl && vr;
222	}
223
224	ungetch();
225	return vl;
226}
227
228/*
229 * bor : xor { "|" xor }
230 */
231static int
232bor(int mayeval)
233{
234	int vl, vr, c, cr;
235
236	vl = xor(mayeval);
237	while ((c = skipws()) == '|') {
238		cr = getch();
239		ungetch();
240		if (cr == '|')
241			break;
242		vr = xor(mayeval);
243		vl |= vr;
244	}
245	ungetch();
246	return (vl);
247}
248
249/*
250 * xor : band { "^" band }
251 */
252static int
253xor(int mayeval)
254{
255	int vl, vr, c;
256
257	vl = band(mayeval);
258	while ((c = skipws()) == '^') {
259		vr = band(mayeval);
260		vl ^= vr;
261	}
262	ungetch();
263	return (vl);
264}
265
266/*
267 * band : eqrel { "&" eqrel }
268 */
269static int
270band(int mayeval)
271{
272	int c, cr, vl, vr;
273
274	vl = eqrel(mayeval);
275	while ((c = skipws()) == '&') {
276		cr = getch();
277		ungetch();
278		if (cr == '&')
279			break;
280		vr = eqrel(mayeval);
281		vl &= vr;
282	}
283	ungetch();
284	return vl;
285}
286
287/*
288 * eqrel : nerel { ("==" | "!=" ) nerel }
289 */
290static int
291eqrel(int mayeval)
292{
293	int vl, vr, c, cr;
294
295	vl = nerel(mayeval);
296	while ((c = skipws()) == '!' || c == '=') {
297		if ((cr = getch()) != '=') {
298			ungetch();
299			break;
300		}
301		vr = nerel(mayeval);
302		switch (c) {
303		case '=':
304			vl = (vl == vr);
305			break;
306		case '!':
307			vl = (vl != vr);
308			break;
309		}
310	}
311	ungetch();
312	return vl;
313}
314
315/*
316 * nerel : shift { ("<=" | ">=" | "<" | ">") shift }
317 */
318static int
319nerel(int mayeval)
320{
321	int vl, vr, c, cr;
322
323	vl = shift(mayeval);
324	while ((c = skipws()) == '<' || c == '>') {
325		if ((cr = getch()) != '=') {
326			ungetch();
327			cr = '\0';
328		}
329		vr = shift(mayeval);
330		switch (c) {
331		case '<':
332			vl = (cr == '\0') ? (vl < vr) : (vl <= vr);
333			break;
334		case '>':
335			vl = (cr == '\0') ? (vl > vr) : (vl >= vr);
336			break;
337		}
338	}
339	ungetch();
340	return vl;
341}
342
343/*
344 * shift : primary { ("<<" | ">>") primary }
345 */
346static int
347shift(int mayeval)
348{
349	int vl, vr, c;
350
351	vl = primary(mayeval);
352	while (((c = skipws()) == '<' || c == '>') && getch() == c) {
353		vr = primary(mayeval);
354
355		if (c == '<')
356			vl <<= vr;
357		else
358			vl >>= vr;
359	}
360
361	if (c == '<' || c == '>')
362		ungetch();
363	ungetch();
364	return vl;
365}
366
367/*
368 * primary : term { ("+" | "-") term }
369 */
370static int
371primary(int mayeval)
372{
373	int c, vl, vr;
374
375	vl = term(mayeval);
376	while ((c = skipws()) == '+' || c == '-') {
377		vr = term(mayeval);
378
379		if (c == '+')
380			vl += vr;
381		else
382			vl -= vr;
383	}
384
385	ungetch();
386	return vl;
387}
388
389/*
390 * term : exponent { ("*" | "/" | "%") exponent }
391 */
392static int
393term(int mayeval)
394{
395	int c, vl, vr;
396
397	vl = exponent(mayeval);
398	while ((c = skipws()) == '*' || c == '/' || c == '%') {
399		vr = exponent(mayeval);
400
401		switch (c) {
402		case '*':
403			vl *= vr;
404			break;
405		case '/':
406			if (!mayeval)
407				/* short-circuit */;
408			else if (vr == 0)
409				errx(1, "division by zero in eval.");
410			else
411				vl /= vr;
412			break;
413		case '%':
414			if (!mayeval)
415				/* short-circuit */;
416			else if (vr == 0)
417				errx(1, "modulo zero in eval.");
418			else
419				vl %= vr;
420			break;
421		}
422	}
423	ungetch();
424	return vl;
425}
426
427/*
428 * exponent : unary { "**" exponent }
429 */
430static int
431exponent(int mayeval)
432{
433	int c, vl, vr, n;
434
435	vl = unary(mayeval);
436	while ((c = skipws()) == '*') {
437		if (getch() != '*') {
438			ungetch();
439			break;
440		}
441		vr = unary(mayeval);
442		n = 1;
443		while (vr-- > 0)
444			n *= vl;
445		return n;
446	}
447
448	ungetch();
449	return vl;
450}
451
452/*
453 * unary : factor | ("+" | "-" | "~" | "!") unary
454 */
455static int
456unary(int mayeval)
457{
458	int val, c;
459
460	if ((c = skipws()) == '+' || c == '-' || c == '~' || c == '!') {
461		val = unary(mayeval);
462
463		switch (c) {
464		case '+':
465			return val;
466		case '-':
467			return -val;
468		case '~':
469			return ~val;
470		case '!':
471			return !val;
472		}
473	}
474
475	ungetch();
476	return factor(mayeval);
477}
478
479/*
480 * factor : constant | '(' query ')'
481 */
482static int
483factor(int mayeval)
484{
485	int val;
486
487	if (skipws() == '(') {
488		val = query(mayeval);
489		if (skipws() != ')')
490			experr("bad factor: missing \")\"");
491		return val;
492	}
493
494	ungetch();
495	return constant(mayeval);
496}
497
498/*
499 * constant: num | 'char'
500 * Note: constant() handles multi-byte constants
501 */
502static int
503constant(int mayeval)
504{
505	int i;
506	int value;
507	int c;
508	int v[sizeof(int)];
509
510	if (skipws() != '\'') {
511		ungetch();
512		return num(mayeval);
513	}
514	for (i = 0; i < (ssize_t)sizeof(int); i++) {
515		if ((c = getch()) == '\'') {
516			ungetch();
517			break;
518		}
519		if (c == '\\') {
520			switch (c = getch()) {
521			case '0':
522			case '1':
523			case '2':
524			case '3':
525			case '4':
526			case '5':
527			case '6':
528			case '7':
529				ungetch();
530				c = num(mayeval);
531				break;
532			case 'n':
533				c = 012;
534				break;
535			case 'r':
536				c = 015;
537				break;
538			case 't':
539				c = 011;
540				break;
541			case 'b':
542				c = 010;
543				break;
544			case 'f':
545				c = 014;
546				break;
547			}
548		}
549		v[i] = c;
550	}
551	if (i == 0 || getch() != '\'')
552		experr("illegal character constant");
553	for (value = 0; --i >= 0;) {
554		value <<= 8;
555		value += v[i];
556	}
557	return value;
558}
559
560/*
561 * num : digit | num digit
562 */
563static int
564num(int mayeval __unused)
565{
566	int rval, c, base;
567	int ndig;
568
569	rval = 0;
570	ndig = 0;
571	c = skipws();
572	if (c == '0') {
573		c = skipws();
574		if (c == 'x' || c == 'X') {
575			base = HEX;
576			c = skipws();
577		} else {
578			base = OCTAL;
579			ndig++;
580		}
581	} else
582		base = DECIMAL;
583	for(;;) {
584		switch(c) {
585			case '8': case '9':
586				if (base == OCTAL)
587					goto bad_digit;
588				/*FALLTHRU*/
589			case '0': case '1': case '2': case '3':
590			case '4': case '5': case '6': case '7':
591				rval *= base;
592				rval += c - '0';
593				break;
594			case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
595				c = tolower(c);
596			case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
597				if (base == HEX) {
598					rval *= base;
599					rval += c - 'a' + 10;
600					break;
601				}
602				/*FALLTHRU*/
603			default:
604				goto bad_digit;
605		}
606		c = getch();
607		ndig++;
608	}
609bad_digit:
610	ungetch();
611
612	if (ndig == 0)
613		experr("bad constant");
614
615	return rval;
616}
617
618/*
619 * Skip over any white space and return terminating char.
620 */
621static int
622skipws(void)
623{
624	int c;
625
626	while ((c = getch()) <= ' ' && c > EOS)
627		;
628	return c;
629}
630
631/*
632 * resets environment to eval(), prints an error
633 * and forces eval to return FALSE.
634 */
635static void
636experr(const char *msg)
637{
638	printf("m4: %s in expr %s.\n", msg, where);
639	longjmp(expjump, -1);
640}
641