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