expr.c revision 1.7 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