]> CyberLeo.Net >> Repos - FreeBSD/releng/8.1.git/blob - usr.bin/m4/expr.c
Copy stable/8 to releng/8.1 in preparation for 8.1-RC1.
[FreeBSD/releng/8.1.git] / usr.bin / m4 / expr.c
1 /*      $OpenBSD: expr.c,v 1.14 2002/04/26 16:15:16 espie Exp $ */
2 /*      $NetBSD: expr.c,v 1.7 1995/09/28 05:37:31 tls 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. All advertising materials mentioning features or use of this software
20  *    must display the following acknowledgement:
21  *      This product includes software developed by the University of
22  *      California, Berkeley and its contributors.
23  * 4. Neither the name of the University nor the names of its contributors
24  *    may be used to endorse or promote products derived from this software
25  *    without specific prior written permission.
26  *
27  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
28  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
29  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
30  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
31  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
32  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
33  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
34  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
35  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
36  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
37  * SUCH DAMAGE.
38  */
39
40 #ifndef lint
41 #if 0
42 static char sccsid[] = "@(#)expr.c      8.2 (Berkeley) 4/29/95";
43 #else
44 #if 0
45 static char rcsid[] = "$OpenBSD: expr.c,v 1.14 2002/04/26 16:15:16 espie Exp $";
46 #endif
47 #endif
48 #endif /* not lint */
49
50 #include <sys/cdefs.h>
51 __FBSDID("$FreeBSD$");
52
53 #include <sys/types.h>
54 #include <ctype.h>
55 #include <err.h>
56 #include <stddef.h>
57 #include <stdio.h>
58 #include "mdef.h"
59 #include "extern.h"
60
61 /*
62  *      expression evaluator: performs a standard recursive
63  *      descent parse to evaluate any expression permissible
64  *      within the following grammar:
65  *
66  *      expr    :       query EOS
67  *      query   :       lor
68  *              |       lor "?" query ":" query
69  *      lor     :       land { "||" land }
70  *      land    :       bor { "&&" bor }
71  *      bor     :       xor { "|" xor }
72  *      xor     :       band { "^" eqrel }
73  *      band    :       eqrel { "&" eqrel }
74  *      eqrel   :       nerel { ("==" | "!=") nerel }
75  *      nerel   :       shift { ("<" | ">" | "<=" | ">=") shift }
76  *      shift   :       primary { ("<<" | ">>") primary }
77  *      primary :       term { ("+" | "-") term }
78  *      term    :       exp { ("*" | "/" | "%") exp }
79  *      exp     :       unary { "**" unary }
80  *      unary   :       factor
81  *              |       ("+" | "-" | "~" | "!") unary
82  *      factor  :       constant
83  *              |       "(" query ")"
84  *      constant:       num
85  *              |       "'" CHAR "'"
86  *      num     :       DIGIT
87  *              |       DIGIT num
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 EQL     0
99 #define NEQ     1
100 #define LSS     2
101 #define LEQ     3
102 #define GTR     4
103 #define GEQ     5
104 #define OCTAL   8
105 #define DECIMAL 10
106 #define HEX     16
107
108 static const char *nxtch;                      /* Parser scan pointer */
109 static const char *where;
110
111 static int query(int mayeval);
112 static int lor(int mayeval);
113 static int land(int mayeval);
114 static int bor(int mayeval);
115 static int xor(int mayeval);
116 static int band(int mayeval);
117 static int eqrel(int mayeval);
118 static int nerel(int mayeval);
119 static int shift(int mayeval);
120 static int primary(int mayeval);
121 static int term(int mayeval);
122 static int exp(int mayeval);
123 static int unary(int mayeval);
124 static int factor(int mayeval);
125 static int constant(int mayeval);
126 static int num(int mayeval);
127 static int geteqrel(int mayeval);
128 static int skipws(void);
129 static void experr(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(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(1);
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(int mayeval)
168 {
169         int result, true_val, false_val;
170
171         result = lor(mayeval);
172         if (skipws() != '?') {
173                 ungetch();
174                 return result;
175         }
176
177         true_val = query(result);
178         if (skipws() != ':')
179                 experr("bad query: missing \":\"");
180
181         false_val = query(!result);
182         return result ? true_val : false_val;
183 }
184
185 /*
186  * lor : land { '||' land }
187  */
188 static int
189 lor(int mayeval)
190 {
191         int c, vl, vr;
192
193         vl = land(mayeval);
194         while ((c = skipws()) == '|') {
195                 if (getch() != '|') {
196                         ungetch();
197                         break;
198                 }
199                 if (vl != 0)
200                         mayeval = 0;
201                 vr = land(mayeval);
202                 vl = vl || vr;
203         }
204
205         ungetch();
206         return vl;
207 }
208
209 /*
210  * land : not { '&&' not }
211  */
212 static int
213 land(int mayeval)
214 {
215         int c, vl, vr;
216
217         vl = bor(mayeval);
218         while ((c = skipws()) == '&') {
219                 if (getch() != '&') {
220                         ungetch();
221                         break;
222                 }
223                 if (vl == 0)
224                         mayeval = 0;
225                 vr = bor(mayeval);
226                 vl = vl && vr;
227         }
228
229         ungetch();
230         return vl;
231 }
232
233 /*
234  * bor : xor { "|" xor }
235  */
236 static int
237 bor(int mayeval)
238 {
239         int vl, vr, c, cr;
240
241         vl = xor(mayeval);
242         while ((c = skipws()) == '|') {
243                 cr = getch();
244                 ungetch();
245                 if (cr == '|')
246                         break;
247                 vr = xor(mayeval);
248                 vl |= vr;
249         }
250         ungetch();
251         return (vl);
252 }
253
254 /*
255  * xor : band { "^" band }
256  */
257 static int
258 xor(int mayeval)
259 {
260         int vl, vr, c;
261
262         vl = band(mayeval);
263         while ((c = skipws()) == '^') {
264                 vr = band(mayeval);
265                 vl ^= vr;
266         }
267         ungetch();
268         return (vl);
269 }
270
271 /*
272  * band : eqrel { "&" eqrel }
273  */
274 static int
275 band(int mayeval)
276 {
277         int c, cr, vl, vr;
278
279         vl = eqrel(mayeval);
280         while ((c = skipws()) == '&') {
281                 cr = getch();
282                 ungetch();
283                 if (cr == '&')
284                         break;
285                 vr = eqrel(mayeval);
286                 vl &= vr;
287         }
288         ungetch();
289         return vl;
290 }
291
292 /*
293  * eqrel : nerel { ("==" | "!=" ) nerel }
294  */
295 static int
296 eqrel(int mayeval)
297 {
298         int vl, vr, c, cr;
299
300         vl = nerel(mayeval);
301         while ((c = skipws()) == '!' || c == '=') {
302                 if ((cr = getch()) != '=') {
303                         ungetch();
304                         break;
305                 }
306                 vr = nerel(mayeval);
307                 switch (c) {
308                 case '=':
309                         vl = (vl == vr);
310                         break;
311                 case '!':
312                         vl = (vl != vr);
313                         break;
314                 }
315         }
316         ungetch();
317         return vl;
318 }
319
320 /*
321  * nerel : shift { ("<=" | ">=" | "<" | ">") shift }
322  */
323 static int
324 nerel(int mayeval)
325 {
326         int vl, vr, c, cr;
327
328         vl = shift(mayeval);
329         while ((c = skipws()) == '<' || c == '>') {
330                 if ((cr = getch()) != '=') {
331                         ungetch();
332                         cr = '\0';
333                 }
334                 vr = shift(mayeval);
335                 switch (c) {
336                 case '<':
337                         vl = (cr == '\0') ? (vl < vr) : (vl <= vr);
338                         break;
339                 case '>':
340                         vl = (cr == '\0') ? (vl > vr) : (vl >= vr);
341                         break;
342                 }
343         }
344         ungetch();
345         return vl;
346 }
347
348 /*
349  * shift : primary { ("<<" | ">>") primary }
350  */
351 static int
352 shift(int mayeval)
353 {
354         int vl, vr, c;
355
356         vl = primary(mayeval);
357         while (((c = skipws()) == '<' || c == '>') && getch() == c) {
358                 vr = primary(mayeval);
359
360                 if (c == '<')
361                         vl <<= vr;
362                 else
363                         vl >>= vr;
364         }
365
366         if (c == '<' || c == '>')
367                 ungetch();
368         ungetch();
369         return vl;
370 }
371
372 /*
373  * primary : term { ("+" | "-") term }
374  */
375 static int
376 primary(int mayeval)
377 {
378         int c, vl, vr;
379
380         vl = term(mayeval);
381         while ((c = skipws()) == '+' || c == '-') {
382                 vr = term(mayeval);
383
384                 if (c == '+')
385                         vl += vr;
386                 else
387                         vl -= vr;
388         }
389
390         ungetch();
391         return vl;
392 }
393
394 /*
395  * term : exp { ("*" | "/" | "%") exp }
396  */
397 static int
398 term(int mayeval)
399 {
400         int c, vl, vr;
401
402         vl = exp(mayeval);
403         while ((c = skipws()) == '*' || c == '/' || c == '%') {
404                 vr = exp(mayeval);
405
406                 switch (c) {
407                 case '*':
408                         vl *= vr;
409                         break;
410                 case '/':
411                         if (!mayeval)
412                                 /* short-circuit */;
413                         else if (vr == 0)
414                                 errx(1, "division by zero in eval.");
415                         else
416                                 vl /= vr;
417                         break;
418                 case '%':
419                         if (!mayeval)
420                                 /* short-circuit */;
421                         else if (vr == 0)
422                                 errx(1, "modulo zero in eval.");
423                         else
424                                 vl %= vr;
425                         break;
426                 }
427         }
428         ungetch();
429         return vl;
430 }
431
432 /*
433  * exp : unary { "**" exp }
434  */
435 static int
436 exp(int mayeval)
437 {
438         int c, vl, vr, n;
439
440         vl = unary(mayeval);
441         while ((c = skipws()) == '*') {
442                 if (getch() != '*') {
443                         ungetch();
444                         break;
445                 }
446                 vr = unary(mayeval);
447                 n = 1;
448                 while (vr-- > 0)
449                         n *= vl;
450                 return n;
451         }
452
453         ungetch();
454         return vl;
455 }
456
457 /*
458  * unary : factor | ("+" | "-" | "~" | "!") unary
459  */
460 static int
461 unary(int mayeval)
462 {
463         int val, c;
464
465         if ((c = skipws()) == '+' || c == '-' || c == '~' || c == '!') {
466                 val = unary(mayeval);
467
468                 switch (c) {
469                 case '+':
470                         return val;
471                 case '-':
472                         return -val;
473                 case '~':
474                         return ~val;
475                 case '!':
476                         return !val;
477                 }
478         }
479
480         ungetch();
481         return factor(mayeval);
482 }
483
484 /*
485  * factor : constant | '(' query ')'
486  */
487 static int
488 factor(int mayeval)
489 {
490         int val;
491
492         if (skipws() == '(') {
493                 val = query(mayeval);
494                 if (skipws() != ')')
495                         experr("bad factor: missing \")\"");
496                 return val;
497         }
498
499         ungetch();
500         return constant(mayeval);
501 }
502
503 /*
504  * constant: num | 'char'
505  * Note: constant() handles multi-byte constants
506  */
507 static int
508 constant(int mayeval)
509 {
510         int i;
511         int value;
512         int c;
513         int v[sizeof(int)];
514
515         if (skipws() != '\'') {
516                 ungetch();
517                 return num(mayeval);
518         }
519         for (i = 0; i < (ssize_t)sizeof(int); i++) {
520                 if ((c = getch()) == '\'') {
521                         ungetch();
522                         break;
523                 }
524                 if (c == '\\') {
525                         switch (c = getch()) {
526                         case '0':
527                         case '1':
528                         case '2':
529                         case '3':
530                         case '4':
531                         case '5':
532                         case '6':
533                         case '7':
534                                 ungetch();
535                                 c = num(mayeval);
536                                 break;
537                         case 'n':
538                                 c = 012;
539                                 break;
540                         case 'r':
541                                 c = 015;
542                                 break;
543                         case 't':
544                                 c = 011;
545                                 break;
546                         case 'b':
547                                 c = 010;
548                                 break;
549                         case 'f':
550                                 c = 014;
551                                 break;
552                         }
553                 }
554                 v[i] = c;
555         }
556         if (i == 0 || getch() != '\'')
557                 experr("illegal character constant");
558         for (value = 0; --i >= 0;) {
559                 value <<= 8;
560                 value += v[i];
561         }
562         return value;
563 }
564
565 /*
566  * num : digit | num digit
567  */
568 static int
569 num(int mayeval)
570 {
571         int rval, c, base;
572         int ndig;
573
574         rval = 0;
575         ndig = 0;
576         c = skipws();
577         if (c == '0') {
578                 c = skipws();
579                 if (c == 'x' || c == 'X') {
580                         base = HEX;
581                         c = skipws();
582                 } else {
583                         base = OCTAL;
584                         ndig++;
585                 }
586         } else
587                 base = DECIMAL;
588         for(;;) {
589                 switch(c) {
590                         case '8': case '9':
591                                 if (base == OCTAL)
592                                         goto bad_digit;
593                                 /*FALLTHRU*/
594                         case '0': case '1': case '2': case '3':
595                         case '4': case '5': case '6': case '7':
596                                 rval *= base;
597                                 rval += c - '0';
598                                 break;
599                         case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
600                                 c = tolower(c);
601                         case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
602                                 if (base == HEX) {
603                                         rval *= base;
604                                         rval += c - 'a' + 10;
605                                         break;
606                                 }
607                                 /*FALLTHRU*/
608                         default:
609                                 goto bad_digit;
610                 }
611                 c = getch();
612                 ndig++;
613         }
614 bad_digit:
615         ungetch();
616
617         if (ndig == 0)
618                 experr("bad constant");
619
620         return rval;
621 }
622
623 /*
624  * Skip over any white space and return terminating char.
625  */
626 static int
627 skipws(void)
628 {
629         int c;
630
631         while ((c = getch()) <= ' ' && c > EOS)
632                 ;
633         return c;
634 }
635
636 /*
637  * resets environment to eval(), prints an error
638  * and forces eval to return FALSE.
639  */
640 static void
641 experr(const char *msg)
642 {
643         printf("m4: %s in expr %s.\n", msg, where);
644         longjmp(expjump, -1);
645 }