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