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