mandoc(1): Update to 1.9.13.
[dragonfly.git] / usr.bin / m4 / expr.c
blob18129baf6beb79ee868841811ade505dc6aa2b22
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 $ */
4 /*
5 * Copyright (c) 1989, 1993
6 * The Regents of the University of California. All rights reserved.
8 * This code is derived from software contributed to Berkeley by
9 * Ozan Yigit at York University.
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.
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.
39 * @(#)expr.c 8.2 (Berkeley) 4/29/95
40 * $OpenBSD: expr.c,v 1.14 2002/04/26 16:15:16 espie Exp $
41 * $FreeBSD: src/usr.bin/m4/expr.c,v 1.14 2004/05/01 03:59:43 smkelly Exp $
42 * $DragonFly: src/usr.bin/m4/expr.c,v 1.4 2006/12/27 21:29:02 pavalos Exp $
45 #include <sys/types.h>
46 #include <ctype.h>
47 #include <err.h>
48 #include <stddef.h>
49 #include <stdio.h>
50 #include "mdef.h"
51 #include "extern.h"
54 * expression evaluator: performs a standard recursive
55 * descent parse to evaluate any expression permissible
56 * within the following grammar:
58 * expr : query EOS
59 * query : lor
60 * | lor "?" query ":" query
61 * lor : land { "||" land }
62 * land : bor { "&&" bor }
63 * bor : xor { "|" xor }
64 * xor : band { "^" eqrel }
65 * band : eqrel { "&" eqrel }
66 * eqrel : nerel { ("==" | "!=") nerel }
67 * nerel : shift { ("<" | ">" | "<=" | ">=") shift }
68 * shift : primary { ("<<" | ">>") primary }
69 * primary : term { ("+" | "-") term }
70 * term : exp { ("*" | "/" | "%") exp }
71 * exp : unary { "**" unary }
72 * unary : factor
73 * | ("+" | "-" | "~" | "!") unary
74 * factor : constant
75 * | "(" query ")"
76 * constant: num
77 * | "'" CHAR "'"
78 * num : DIGIT
79 * | DIGIT num
82 * This expression evaluator is lifted from a public-domain
83 * C Pre-Processor included with the DECUS C Compiler distribution.
84 * It is hacked somewhat to be suitable for m4.
86 * Originally by: Mike Lutz
87 * Bob Harper
90 #define EQL 0
91 #define NEQ 1
92 #define LSS 2
93 #define LEQ 3
94 #define GTR 4
95 #define GEQ 5
96 #define OCTAL 8
97 #define DECIMAL 10
98 #define HEX 16
100 static const char *nxtch; /* Parser scan pointer */
101 static const char *where;
103 static int query(int mayeval);
104 static int lor(int mayeval);
105 static int land(int mayeval);
106 static int bor(int mayeval);
107 static int xor(int mayeval);
108 static int band(int mayeval);
109 static int eqrel(int mayeval);
110 static int nerel(int mayeval);
111 static int shift(int mayeval);
112 static int primary(int mayeval);
113 static int term(int mayeval);
114 static int expx(int mayeval);
115 static int unary(int mayeval);
116 static int factor(int mayeval);
117 static int constant(int mayeval);
118 static int num(int mayeval);
119 static int skipws(void);
120 static void experr(const char *);
123 * For longjmp
125 #include <setjmp.h>
126 static jmp_buf expjump;
129 * macros:
130 * ungetch - Put back the last character examined.
131 * getch - return the next character from expr string.
133 #define ungetch() nxtch--
134 #define getch() *nxtch++
137 expr(const char *expbuf)
139 int rval;
141 nxtch = expbuf;
142 where = expbuf;
143 if (setjmp(expjump) != 0)
144 return FALSE;
146 rval = query(1);
147 if (skipws() == EOS)
148 return rval;
150 printf("m4: ill-formed expression.\n");
151 return FALSE;
155 * query : lor | lor '?' query ':' query
157 static int
158 query(int mayeval)
160 int result, true_val, false_val;
162 result = lor(mayeval);
163 if (skipws() != '?') {
164 ungetch();
165 return result;
168 true_val = query(result);
169 if (skipws() != ':')
170 experr("bad query: missing \":\"");
172 false_val = query(!result);
173 return result ? true_val : false_val;
177 * lor : land { '||' land }
179 static int
180 lor(int mayeval)
182 int c, vl, vr;
184 vl = land(mayeval);
185 while ((c = skipws()) == '|') {
186 if (getch() != '|') {
187 ungetch();
188 break;
190 if (vl != 0)
191 mayeval = 0;
192 vr = land(mayeval);
193 vl = vl || vr;
196 ungetch();
197 return vl;
201 * land : not { '&&' not }
203 static int
204 land(int mayeval)
206 int c, vl, vr;
208 vl = bor(mayeval);
209 while ((c = skipws()) == '&') {
210 if (getch() != '&') {
211 ungetch();
212 break;
214 if (vl == 0)
215 mayeval = 0;
216 vr = bor(mayeval);
217 vl = vl && vr;
220 ungetch();
221 return vl;
225 * bor : xor { "|" xor }
227 static int
228 bor(int mayeval)
230 int vl, vr, c, cr;
232 vl = xor(mayeval);
233 while ((c = skipws()) == '|') {
234 cr = getch();
235 ungetch();
236 if (cr == '|')
237 break;
238 vr = xor(mayeval);
239 vl |= vr;
241 ungetch();
242 return (vl);
246 * xor : band { "^" band }
248 static int
249 xor(int mayeval)
251 int vl, vr, c;
253 vl = band(mayeval);
254 while ((c = skipws()) == '^') {
255 vr = band(mayeval);
256 vl ^= vr;
258 ungetch();
259 return (vl);
263 * band : eqrel { "&" eqrel }
265 static int
266 band(int mayeval)
268 int c, cr, vl, vr;
270 vl = eqrel(mayeval);
271 while ((c = skipws()) == '&') {
272 cr = getch();
273 ungetch();
274 if (cr == '&')
275 break;
276 vr = eqrel(mayeval);
277 vl &= vr;
279 ungetch();
280 return vl;
284 * eqrel : nerel { ("==" | "!=" ) nerel }
286 static int
287 eqrel(int mayeval)
289 int vl, vr, c, cr;
291 vl = nerel(mayeval);
292 while ((c = skipws()) == '!' || c == '=') {
293 if ((cr = getch()) != '=') {
294 ungetch();
295 break;
297 vr = nerel(mayeval);
298 switch (c) {
299 case '=':
300 vl = (vl == vr);
301 break;
302 case '!':
303 vl = (vl != vr);
304 break;
307 ungetch();
308 return vl;
312 * nerel : shift { ("<=" | ">=" | "<" | ">") shift }
314 static int
315 nerel(int mayeval)
317 int vl, vr, c, cr;
319 vl = shift(mayeval);
320 while ((c = skipws()) == '<' || c == '>') {
321 if ((cr = getch()) != '=') {
322 ungetch();
323 cr = '\0';
325 vr = shift(mayeval);
326 switch (c) {
327 case '<':
328 vl = (cr == '\0') ? (vl < vr) : (vl <= vr);
329 break;
330 case '>':
331 vl = (cr == '\0') ? (vl > vr) : (vl >= vr);
332 break;
335 ungetch();
336 return vl;
340 * shift : primary { ("<<" | ">>") primary }
342 static int
343 shift(int mayeval)
345 int vl, vr, c;
347 vl = primary(mayeval);
348 while (((c = skipws()) == '<' || c == '>') && getch() == c) {
349 vr = primary(mayeval);
351 if (c == '<')
352 vl <<= vr;
353 else
354 vl >>= vr;
357 if (c == '<' || c == '>')
358 ungetch();
359 ungetch();
360 return vl;
364 * primary : term { ("+" | "-") term }
366 static int
367 primary(int mayeval)
369 int c, vl, vr;
371 vl = term(mayeval);
372 while ((c = skipws()) == '+' || c == '-') {
373 vr = term(mayeval);
375 if (c == '+')
376 vl += vr;
377 else
378 vl -= vr;
381 ungetch();
382 return vl;
386 * term : exp { ("*" | "/" | "%") exp }
388 static int
389 term(int mayeval)
391 int c, vl, vr;
393 vl = expx(mayeval);
394 while ((c = skipws()) == '*' || c == '/' || c == '%') {
395 vr = expx(mayeval);
397 switch (c) {
398 case '*':
399 vl *= vr;
400 break;
401 case '/':
402 if (!mayeval)
403 /* short-circuit */;
404 else if (vr == 0)
405 errx(1, "division by zero in eval.");
406 else
407 vl /= vr;
408 break;
409 case '%':
410 if (!mayeval)
411 /* short-circuit */;
412 else if (vr == 0)
413 errx(1, "modulo zero in eval.");
414 else
415 vl %= vr;
416 break;
419 ungetch();
420 return vl;
424 * exp : unary { "**" exp }
426 static int
427 expx(int mayeval)
429 int c, vl, vr, n;
431 vl = unary(mayeval);
432 while ((c = skipws()) == '*') {
433 if (getch() != '*') {
434 ungetch();
435 break;
437 vr = unary(mayeval);
438 n = 1;
439 while (vr-- > 0)
440 n *= vl;
441 return n;
444 ungetch();
445 return vl;
449 * unary : factor | ("+" | "-" | "~" | "!") unary
451 static int
452 unary(int mayeval)
454 int val, c;
456 if ((c = skipws()) == '+' || c == '-' || c == '~' || c == '!') {
457 val = unary(mayeval);
459 switch (c) {
460 case '+':
461 return val;
462 case '-':
463 return -val;
464 case '~':
465 return ~val;
466 case '!':
467 return !val;
471 ungetch();
472 return factor(mayeval);
476 * factor : constant | '(' query ')'
478 static int
479 factor(int mayeval)
481 int val;
483 if (skipws() == '(') {
484 val = query(mayeval);
485 if (skipws() != ')')
486 experr("bad factor: missing \")\"");
487 return val;
490 ungetch();
491 return constant(mayeval);
495 * constant: num | 'char'
496 * Note: constant() handles multi-byte constants
498 static int
499 constant(int mayeval)
501 int i;
502 int value;
503 int c;
504 int v[sizeof(int)];
506 if (skipws() != '\'') {
507 ungetch();
508 return num(mayeval);
510 for (i = 0; i < (ssize_t)sizeof(int); i++) {
511 if ((c = getch()) == '\'') {
512 ungetch();
513 break;
515 if (c == '\\') {
516 switch (c = getch()) {
517 case '0':
518 case '1':
519 case '2':
520 case '3':
521 case '4':
522 case '5':
523 case '6':
524 case '7':
525 ungetch();
526 c = num(mayeval);
527 break;
528 case 'n':
529 c = 012;
530 break;
531 case 'r':
532 c = 015;
533 break;
534 case 't':
535 c = 011;
536 break;
537 case 'b':
538 c = 010;
539 break;
540 case 'f':
541 c = 014;
542 break;
545 v[i] = c;
547 if (i == 0 || getch() != '\'')
548 experr("illegal character constant");
549 for (value = 0; --i >= 0;) {
550 value <<= 8;
551 value += v[i];
553 return value;
557 * num : digit | num digit
559 static int
560 num(int mayeval __unused)
562 int rval, c, base;
563 int ndig;
565 rval = 0;
566 ndig = 0;
567 c = skipws();
568 if (c == '0') {
569 c = skipws();
570 if (c == 'x' || c == 'X') {
571 base = HEX;
572 c = skipws();
573 } else {
574 base = OCTAL;
575 ndig++;
577 } else
578 base = DECIMAL;
579 for(;;) {
580 switch(c) {
581 case '8': case '9':
582 if (base == OCTAL)
583 goto bad_digit;
584 /*FALLTHRU*/
585 case '0': case '1': case '2': case '3':
586 case '4': case '5': case '6': case '7':
587 rval *= base;
588 rval += c - '0';
589 break;
590 case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
591 c = tolower(c);
592 case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
593 if (base == HEX) {
594 rval *= base;
595 rval += c - 'a' + 10;
596 break;
598 /*FALLTHRU*/
599 default:
600 goto bad_digit;
602 c = getch();
603 ndig++;
605 bad_digit:
606 ungetch();
608 if (ndig == 0)
609 experr("bad constant");
611 return rval;
615 * Skip over any white space and return terminating char.
617 static int
618 skipws(void)
620 int c;
622 while ((c = getch()) <= ' ' && c > EOS)
624 return c;
628 * resets environment to eval(), prints an error
629 * and forces eval to return FALSE.
631 static void
632 experr(const char *msg)
634 printf("m4: %s in expr %s.\n", msg, where);
635 longjmp(expjump, -1);