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