expr.c revision 1.16 1 /* $NetBSD: expr.c,v 1.16 2004/06/20 22:20:15 jmc Exp $ */
2 /* $OpenBSD: expr.c,v 1.11 2000/01/11 14:00:57 espie 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 * 3. 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 #if HAVE_NBTOOL_CONFIG_H
37 #include "nbtool_config.h"
38 #endif
39
40 #include <sys/cdefs.h>
41 #if defined(__RCSID) && !defined(lint)
42 #if 0
43 static char sccsid[] = "@(#)expr.c 8.2 (Berkeley) 4/29/95";
44 #else
45 __RCSID("$NetBSD: expr.c,v 1.16 2004/06/20 22:20:15 jmc Exp $");
46 #endif
47 #endif /* not lint */
48
49 #include <sys/types.h>
50 #include <ctype.h>
51 #include <stddef.h>
52 #include <stdio.h>
53 #include "mdef.h"
54 #include "extern.h"
55
56 /*
57 * expression evaluator: performs a standard recursive
58 * descent parse to evaluate any expression permissible
59 * within the following grammar:
60 *
61 * expr : query EOS
62 * query : lor
63 * | lor "?" query ":" query
64 * lor : land { "||" land }
65 * land : not { "&&" not }
66 * not : eqrel
67 * | '!' not
68 * eqrel : shift { eqrelop shift }
69 * shift : primary { shop primary }
70 * primary : term { addop term }
71 * term : exp { mulop exp }
72 * exp : unary { expop unary }
73 * unary : factor
74 * | unop unary
75 * factor : constant
76 * | "(" query ")"
77 * constant: num
78 * | "'" CHAR "'"
79 * num : DIGIT
80 * | DIGIT num
81 * shop : "<<"
82 * | ">>"
83 * eqrel : "="
84 * | "=="
85 * | "!="
86 * | "<"
87 * | ">"
88 * | "<="
89 * | ">="
90 *
91 *
92 * This expression evaluator is lifted from a public-domain
93 * C Pre-Processor included with the DECUS C Compiler distribution.
94 * It is hacked somewhat to be suitable for m4.
95 *
96 * Originally by: Mike Lutz
97 * Bob Harper
98 */
99
100 #define EQL 0
101 #define NEQ 1
102 #define LSS 2
103 #define LEQ 3
104 #define GTR 4
105 #define GEQ 5
106 #define OCTAL 8
107 #define DECIMAL 10
108 #define HEX 16
109
110 static const char *nxtch; /* Parser scan pointer */
111 static const char *where;
112
113 static int query __P((void));
114 static int lor __P((void));
115 static int land __P((void));
116 static int not __P((void));
117 static int eqrel __P((void));
118 static int shift __P((void));
119 static int primary __P((void));
120 static int term __P((void));
121 static int exp __P((void));
122 static int unary __P((void));
123 static int factor __P((void));
124 static int constant __P((void));
125 static int num __P((void));
126 static int geteqrel __P((void));
127 static int skipws __P((void));
128 static void experr __P((const char *));
129
130 /*
131 * For longjmp
132 */
133 #include <setjmp.h>
134 static jmp_buf expjump;
135
136 /*
137 * macros:
138 * ungetch - Put back the last character examined.
139 * getch - return the next character from expr string.
140 */
141 #define ungetch() nxtch--
142 #define getch() *nxtch++
143
144 int
145 expr(expbuf)
146 const char *expbuf;
147 {
148 int rval;
149
150 nxtch = expbuf;
151 where = 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 if (vr == 0)
347 errx(1, "division by zero in eval.");
348 else
349 vl /= vr;
350 break;
351 case '%':
352 if (vr == 0)
353 errx(1, "modulo zero in eval.");
354 else
355 vl %= vr;
356 break;
357 }
358 }
359 ungetch();
360 return vl;
361 }
362
363 /*
364 * <term> := <unary> { <expop> <unary> }
365 */
366 static int
367 exp()
368 {
369 int c, vl, vr, n;
370
371 vl = unary();
372 switch (c = skipws()) {
373
374 case '*':
375 if (getch() != '*') {
376 ungetch();
377 break;
378 }
379
380 case '^':
381 vr = exp();
382 n = 1;
383 while (vr-- > 0)
384 n *= vl;
385 return n;
386 }
387
388 ungetch();
389 return vl;
390 }
391
392 /*
393 * unary : factor | unop unary
394 */
395 static int
396 unary()
397 {
398 int val, c;
399
400 if ((c = skipws()) == '+' || c == '-' || c == '~') {
401 val = unary();
402
403 switch (c) {
404 case '+':
405 return val;
406 case '-':
407 return -val;
408 case '~':
409 return ~val;
410 }
411 }
412
413 ungetch();
414 return factor();
415 }
416
417 /*
418 * factor : constant | '(' query ')'
419 */
420 static int
421 factor()
422 {
423 int val;
424
425 if (skipws() == '(') {
426 val = query();
427 if (skipws() != ')')
428 experr("bad factor");
429 return val;
430 }
431
432 ungetch();
433 return constant();
434 }
435
436 /*
437 * constant: num | 'char'
438 * Note: constant() handles multi-byte constants
439 */
440 static int
441 constant()
442 {
443 int i;
444 int value;
445 char c;
446 int v[sizeof(int)];
447
448 if (skipws() != '\'') {
449 ungetch();
450 return num();
451 }
452 for (i = 0; i < sizeof(int); i++) {
453 if ((c = getch()) == '\'') {
454 ungetch();
455 break;
456 }
457 if (c == '\\') {
458 switch (c = getch()) {
459 case '0':
460 case '1':
461 case '2':
462 case '3':
463 case '4':
464 case '5':
465 case '6':
466 case '7':
467 ungetch();
468 c = num();
469 break;
470 case 'n':
471 c = 012;
472 break;
473 case 'r':
474 c = 015;
475 break;
476 case 't':
477 c = 011;
478 break;
479 case 'b':
480 c = 010;
481 break;
482 case 'f':
483 c = 014;
484 break;
485 }
486 }
487 v[i] = c;
488 }
489 if (i == 0 || getch() != '\'')
490 experr("illegal character constant");
491 for (value = 0; --i >= 0;) {
492 value <<= 8;
493 value += v[i];
494 }
495 return value;
496 }
497
498 /*
499 * num : digit | num digit
500 */
501 static int
502 num()
503 {
504 int rval, c, base;
505 int ndig;
506
507 base = ((c = skipws()) == '0') ? OCTAL : DECIMAL;
508 rval = 0;
509 ndig = 0;
510 if (base == OCTAL) {
511 c = skipws();
512 if (c == 'x' || c == 'X') {
513 base = HEX;
514 c = skipws();
515 } else
516 ndig++;
517 }
518 while ((base == HEX && isxdigit(c)) ||
519 (c >= '0' && c <= (base == OCTAL ? '7' : '9'))) {
520 rval *= base;
521 if (isalpha(c))
522 rval += (tolower(c) - 'a' + 10);
523 else
524 rval += (c - '0');
525 c = getch();
526 ndig++;
527 }
528 ungetch();
529
530 if (ndig == 0)
531 experr("bad constant");
532
533 return rval;
534 }
535
536 /*
537 * eqrel : '=' | '==' | '!=' | '<' | '>' | '<=' | '>='
538 */
539 static int
540 geteqrel()
541 {
542 int c1, c2;
543
544 c1 = skipws();
545 c2 = getch();
546
547 switch (c1) {
548
549 case '=':
550 if (c2 != '=')
551 ungetch();
552 return EQL;
553
554 case '!':
555 if (c2 == '=')
556 return NEQ;
557 ungetch();
558 ungetch();
559 return -1;
560
561 case '<':
562 if (c2 == '=')
563 return LEQ;
564 ungetch();
565 return LSS;
566
567 case '>':
568 if (c2 == '=')
569 return GEQ;
570 ungetch();
571 return GTR;
572
573 default:
574 ungetch();
575 ungetch();
576 return -1;
577 }
578 }
579
580 /*
581 * Skip over any white space and return terminating char.
582 */
583 static int
584 skipws()
585 {
586 char c;
587
588 while ((c = getch()) <= ' ' && c > EOS)
589 ;
590 return c;
591 }
592
593 /*
594 * resets environment to eval(), prints an error
595 * and forces eval to return FALSE.
596 */
597 static void
598 experr(msg)
599 const char *msg;
600 {
601 printf("m4: %s in expr %s.\n", msg, where);
602 longjmp(expjump, -1);
603 }
604