tradcpp: upgrade to 0.5.3
[oweals/cde.git] / cde / util / tradcpp / eval.c
1 /*-
2  * Copyright (c) 2010 The NetBSD Foundation, Inc.
3  * All rights reserved.
4  *
5  * This code is derived from software contributed to The NetBSD Foundation
6  * by David A. Holland.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
18  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
19  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
20  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
21  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27  * POSSIBILITY OF SUCH DAMAGE.
28  */
29
30 #include <stdlib.h>
31 #include <string.h>
32 #include <limits.h>
33 #include <errno.h>
34
35 //#define DEBUG
36 #ifdef DEBUG
37 #include <stdio.h>
38 #endif
39
40 #include "utils.h"
41 #include "array.h"
42 #include "mode.h"
43 #include "place.h"
44 #include "eval.h"
45
46 /*
47  * e ::=
48  *    e1 ? e2 : e3
49  *    e1 || e2
50  *    e1 && e2
51  *    e1 | e2
52  *    e1 ^ e2
53  *    e1 & e2
54  *    e1 == e2  |  e1 != e2
55  *    e1 < e2   |  e1 <= e2  |  e1 > e2  |  e1 >= e2
56  *    e1 << e2  |  e1 >> e2
57  *    e1 + e2   |  e1 - e2
58  *    e1 * e2   |  e1 / e2   |  e1 % e2
59  *    !e  |  ~e  |  -e  |  +e
60  *    ( e )  |  ident
61  */
62
63 enum tokens {
64         T_EOF,          /* end of input */
65         T_VAL,          /* value */
66         T_LPAREN,       /* parens */
67         T_RPAREN,
68         T_PIPEPIPE,     /* operators */
69         T_AMPAMP,
70         T_EQEQ,
71         T_BANGEQ,
72         T_LTEQ,
73         T_GTEQ,
74         T_LTLT,
75         T_GTGT,
76         T_QUES,
77         T_COLON,
78         T_PIPE,
79         T_CARET,
80         T_AMP,
81         T_LT,
82         T_GT,
83         T_PLUS,
84         T_MINUS,
85         T_STAR,
86         T_SLASH,
87         T_PCT,
88         T_BANG,
89         T_TILDE,
90 };
91
92 static const struct {
93         char c1, c2;
94         enum tokens tok;
95 } tokens_2[] = {
96         { '|', '|', T_PIPEPIPE },
97         { '&', '&', T_AMPAMP },
98         { '=', '=', T_EQEQ },
99         { '!', '=', T_BANGEQ },
100         { '<', '=', T_LTEQ },
101         { '>', '=', T_GTEQ },
102         { '<', '<', T_LTLT },
103         { '>', '>', T_GTGT },
104 };
105 static const unsigned num_tokens_2 = HOWMANY(tokens_2);
106
107 static const struct {
108         char c1;
109         enum tokens tok;
110 } tokens_1[] = {
111         { '?', T_QUES },
112         { ':', T_COLON },
113         { '|', T_PIPE },
114         { '^', T_CARET },
115         { '&', T_AMP },
116         { '<', T_LT },
117         { '>', T_GT },
118         { '+', T_PLUS },
119         { '-', T_MINUS },
120         { '*', T_STAR },
121         { '/', T_SLASH },
122         { '%', T_PCT },
123         { '!', T_BANG },
124         { '~', T_TILDE },
125         { '(', T_LPAREN },
126         { ')', T_RPAREN },
127 };
128 static const unsigned num_tokens_1 = HOWMANY(tokens_1);
129
130 struct token {
131         struct place place;
132         enum tokens tok;
133         int val;
134 };
135 DECLARRAY(token, static UNUSED);
136 DEFARRAY(token, static);
137
138 static struct tokenarray tokens;
139
140 static
141 struct token *
142 token_create(const struct place *p, enum tokens tok, int val)
143 {
144         struct token *t;
145
146         t = domalloc(sizeof(*t));
147         t->place = *p;
148         t->tok = tok;
149         t->val = val;
150         return t;
151 }
152
153 static
154 void
155 token_destroy(struct token *t)
156 {
157         dofree(t, sizeof(*t));
158 }
159
160 DESTROYALL_ARRAY(token, );
161
162 #ifdef DEBUG
163 static
164 void
165 printtokens(void)
166 {
167         unsigned i, num;
168         struct token *t;
169
170         fprintf(stderr, "tokens:");
171         num = tokenarray_num(&tokens);
172         for (i=0; i<num; i++) {
173                 t = tokenarray_get(&tokens, i);
174                 switch (t->tok) {
175                     case T_EOF: fprintf(stderr, " <eof>"); break;
176                     case T_VAL: fprintf(stderr, " %d", t->val); break;
177                     case T_LPAREN: fprintf(stderr, " ("); break;
178                     case T_RPAREN: fprintf(stderr, " )"); break;
179                     case T_PIPEPIPE: fprintf(stderr, " ||"); break;
180                     case T_AMPAMP: fprintf(stderr, " &&"); break;
181                     case T_EQEQ: fprintf(stderr, " =="); break;
182                     case T_BANGEQ: fprintf(stderr, " !="); break;
183                     case T_LTEQ: fprintf(stderr, " <="); break;
184                     case T_GTEQ: fprintf(stderr, " >="); break;
185                     case T_LTLT: fprintf(stderr, " <<"); break;
186                     case T_GTGT: fprintf(stderr, " >>"); break;
187                     case T_QUES: fprintf(stderr, " ?"); break;
188                     case T_COLON: fprintf(stderr, " :"); break;
189                     case T_PIPE: fprintf(stderr, " |"); break;
190                     case T_CARET: fprintf(stderr, " ^"); break;
191                     case T_AMP: fprintf(stderr, " &"); break;
192                     case T_LT: fprintf(stderr, " <"); break;
193                     case T_GT: fprintf(stderr, " >"); break;
194                     case T_PLUS: fprintf(stderr, " +"); break;
195                     case T_MINUS: fprintf(stderr, " -"); break;
196                     case T_STAR: fprintf(stderr, " *"); break;
197                     case T_SLASH: fprintf(stderr, " /"); break;
198                     case T_PCT: fprintf(stderr, " %%"); break;
199                     case T_BANG: fprintf(stderr, " !"); break;
200                     case T_TILDE: fprintf(stderr, " ~"); break;
201                 }
202         }
203         fprintf(stderr, "\n");
204 }
205 #endif
206
207 static
208 bool
209 isuop(enum tokens tok)
210 {
211         switch (tok) {
212             case T_BANG:
213             case T_TILDE:
214             case T_MINUS:
215             case T_PLUS:
216                 return true;
217             default:
218                 break;
219         }
220         return false;
221 }
222
223 static
224 bool
225 isbop(enum tokens tok)
226 {
227         switch (tok) {
228             case T_EOF:
229             case T_VAL:
230             case T_LPAREN:
231             case T_RPAREN:
232             case T_COLON:
233             case T_QUES:
234             case T_BANG:
235             case T_TILDE:
236                 return false;
237             default:
238                 break;
239         }
240         return true;
241 }
242
243 static
244 bool
245 isop(enum tokens tok)
246 {
247         switch (tok) {
248             case T_EOF:
249             case T_VAL:
250             case T_LPAREN:
251             case T_RPAREN:
252                 return false;
253             default:
254                 break;
255         }
256         return true;
257 }
258
259 static
260 int
261 getprec(enum tokens tok)
262 {
263         switch (tok) {
264             case T_BANG: case T_TILDE: return -1;
265             case T_STAR: case T_SLASH: case T_PCT: return 0;
266             case T_PLUS: case T_MINUS: return 1;
267             case T_LTLT: case T_GTGT: return 2;
268             case T_LT: case T_LTEQ: case T_GT: case T_GTEQ: return 3;
269             case T_EQEQ: case T_BANGEQ: return 4;
270             case T_AMP: return 5;
271             case T_CARET: return 6;
272             case T_PIPE: return 7;
273             case T_AMPAMP: return 8;
274             case T_PIPEPIPE: return 9;
275             default: break;
276         }
277         return 10;
278 }
279
280 static
281 bool
282 looser(enum tokens t1, enum tokens t2)
283 {
284         return getprec(t1) >= getprec(t2);
285 }
286
287 static
288 int
289 eval_uop(enum tokens op, int val)
290 {
291         switch (op) {
292             case T_BANG: val = !val; break;
293             case T_TILDE: val = (int)~(unsigned)val; break;
294             case T_MINUS: val = -val; break;
295             case T_PLUS: break;
296             default: assert(0); break;
297         }
298         return val;
299 }
300
301 static
302 int
303 eval_bop(struct place *p, int lv, enum tokens op, int rv)
304 {
305         unsigned mask;
306
307         switch (op) {
308             case T_PIPEPIPE: return lv || rv;
309             case T_AMPAMP:   return lv && rv;
310             case T_PIPE:     return (int)((unsigned)lv | (unsigned)rv);
311             case T_CARET:    return (int)((unsigned)lv ^ (unsigned)rv);
312             case T_AMP:      return (int)((unsigned)lv & (unsigned)rv);
313             case T_EQEQ:     return lv == rv;
314             case T_BANGEQ:   return lv != rv;
315             case T_LT:       return lv < rv;
316             case T_GT:       return lv > rv;
317             case T_LTEQ:     return lv <= rv;
318             case T_GTEQ:     return lv >= rv;
319
320             case T_LTLT:
321             case T_GTGT:
322                 if (rv < 0) {
323                         complain(p, "Negative bit-shift");
324                         complain_fail();
325                         rv = 0;
326                 }
327                 if ((unsigned)rv >= CHAR_BIT * sizeof(unsigned)) {
328                         complain(p, "Bit-shift farther than type width");
329                         complain_fail();
330                         rv = 0;
331                 }
332                 if (op == T_LTLT) {
333                         return (int)((unsigned)lv << (unsigned)rv);
334                 }
335                 mask = ((unsigned)-1) << (CHAR_BIT * sizeof(unsigned) - rv);
336                 lv = (int)(((unsigned)lv >> (unsigned)rv) | mask);
337                 return lv;
338
339             case T_MINUS:
340                 if (rv == INT_MIN) {
341                         if (lv == INT_MIN) {
342                                 return 0;
343                         }
344                         lv--;
345                         rv++;
346                 }
347                 rv = -rv;
348                 /* FALLTHROUGH */
349             case T_PLUS:
350                 if (rv > 0 && lv > (INT_MAX - rv)) {
351                         complain(p, "Integer overflow");
352                         complain_fail();
353                         return INT_MAX;
354                 }
355                 if (rv < 0 && lv < (INT_MIN - rv)) {
356                         complain(p, "Integer underflow");
357                         complain_fail();
358                         return INT_MIN;
359                 }
360                 return lv + rv;
361
362             case T_STAR:
363                 if (rv == 0) {
364                         return 0;
365                 }
366                 if (rv == 1) {
367                         return lv;
368                 }
369                 if (rv == -1 && lv == INT_MIN) {
370                         lv++;
371                         lv = -lv;
372                         if (lv == INT_MAX) {
373                                 complain(p, "Integer overflow");
374                                 complain_fail();
375                                 return INT_MAX;
376                         }
377                         lv++;
378                         return lv;
379                 }
380                 if (lv == INT_MIN && rv < 0) {
381                         complain(p, "Integer overflow");
382                         complain_fail();
383                         return INT_MAX;
384                 }
385                 if (lv == INT_MIN && rv > 0) {
386                         complain(p, "Integer underflow");
387                         complain_fail();
388                         return INT_MIN;
389                 }
390                 if (rv < 0) {
391                         rv = -rv;
392                         lv = -lv;
393                 }
394                 if (lv > 0 && lv > INT_MAX / rv) {
395                         complain(p, "Integer overflow");
396                         complain_fail();
397                         return INT_MAX;
398                 }
399                 if (lv < 0 && lv < INT_MIN / rv) {
400                         complain(p, "Integer underflow");
401                         complain_fail();
402                         return INT_MIN;
403                 }
404                 return lv * rv;
405
406             case T_SLASH:
407                 if (rv == 0) {
408                         complain(p, "Division by zero");
409                         complain_fail();
410                         return 0;
411                 }
412                 return lv / rv;
413
414             case T_PCT:
415                 if (rv == 0) {
416                         complain(p, "Modulus by zero");
417                         complain_fail();
418                         return 0;
419                 }
420                 return lv % rv;
421
422             default: assert(0); break;
423         }
424         return 0;
425 }
426
427 static
428 void
429 tryreduce(void)
430 {
431         unsigned num;
432         struct token *t1, *t2, *t3, *t4, *t5, *t6;
433
434         while (1) {
435 #ifdef DEBUG
436                 printtokens();
437 #endif
438                 num = tokenarray_num(&tokens);
439                 t1 = (num >= 1) ? tokenarray_get(&tokens, num-1) : NULL;
440                 t2 = (num >= 2) ? tokenarray_get(&tokens, num-2) : NULL;
441                 t3 = (num >= 3) ? tokenarray_get(&tokens, num-3) : NULL;
442
443                 if (num >= 3 &&
444                     t3->tok == T_LPAREN &&
445                     t2->tok == T_VAL &&
446                     t1->tok == T_RPAREN) {
447                         /* (x) -> x */
448                         t2->place = t3->place;
449                         token_destroy(t1);
450                         token_destroy(t3);
451                         tokenarray_remove(&tokens, num-1);
452                         tokenarray_remove(&tokens, num-3);
453                         continue;
454                 }
455
456                 if (num >= 2 &&
457                     (num == 2 || isop(t3->tok) || t3->tok == T_LPAREN) &&
458                     isuop(t2->tok) &&
459                     t1->tok == T_VAL) {
460                         /* unary operator */
461                         t1->val = eval_uop(t2->tok, t1->val);
462                         t1->place = t2->place;
463                         token_destroy(t2);
464                         tokenarray_remove(&tokens, num-2);
465                         continue;
466                 }
467                 if (num >= 2 &&
468                     (num == 2 || isop(t3->tok) || t3->tok == T_LPAREN) &&
469                     t2->tok != T_LPAREN && t2->tok != T_VAL &&
470                     t1->tok == T_VAL) {
471                         complain(&t2->place, "Invalid unary operator");
472                         complain_fail();
473                         token_destroy(t2);
474                         tokenarray_remove(&tokens, num-2);
475                         continue;
476                 }
477
478                         
479                 t4 = (num >= 4) ? tokenarray_get(&tokens, num-4) : NULL;
480
481                 if (num >= 4 &&
482                     t4->tok == T_VAL &&
483                     isbop(t3->tok) &&
484                     t2->tok == T_VAL) {
485                         /* binary operator */
486                         if (looser(t1->tok, t3->tok)) {
487                                 t4->val = eval_bop(&t3->place,
488                                                    t4->val, t3->tok, t2->val);
489                                 token_destroy(t2);
490                                 token_destroy(t3);
491                                 tokenarray_remove(&tokens, num-2);
492                                 tokenarray_remove(&tokens, num-3);
493                                 continue;
494                         }
495                         break;
496                 }
497
498                 t5 = (num >= 5) ? tokenarray_get(&tokens, num-5) : NULL;
499                 t6 = (num >= 6) ? tokenarray_get(&tokens, num-6) : NULL;
500
501                 if (num >= 6 &&
502                     t6->tok == T_VAL &&
503                     t5->tok == T_QUES &&
504                     t4->tok == T_VAL &&
505                     t3->tok == T_COLON &&
506                     t2->tok == T_VAL &&
507                     !isop(t1->tok)) {
508                         /* conditional expression */
509                         t6->val = t6->val ? t4->val : t2->val;
510                         token_destroy(t2);
511                         token_destroy(t3);
512                         token_destroy(t4);
513                         token_destroy(t5);
514                         tokenarray_remove(&tokens, num-2);
515                         tokenarray_remove(&tokens, num-3);
516                         tokenarray_remove(&tokens, num-4);
517                         tokenarray_remove(&tokens, num-5);
518                         continue;
519                 }
520
521                 if (num >= 2 &&
522                     t2->tok == T_LPAREN &&
523                     t1->tok == T_RPAREN) {
524                         complain(&t1->place, "Value expected within ()");
525                         complain_fail();
526                         t1->tok = T_VAL;
527                         t1->val = 0;
528                         token_destroy(t1);
529                         tokenarray_remove(&tokens, num-1);
530                         continue;
531                 }
532
533                 if (num >= 2 &&
534                     t2->tok == T_VAL &&
535                     t1->tok == T_VAL) {
536                         complain(&t1->place, "Operator expected");
537                         complain_fail();
538                         token_destroy(t1);
539                         tokenarray_remove(&tokens, num-1);
540                         continue;
541                 }
542
543                 if (num >= 2 &&
544                     isop(t2->tok) &&
545                     t1->tok == T_EOF) {
546                         complain(&t1->place, "Value expected after operator");
547                         complain_fail();
548                         token_destroy(t2);
549                         tokenarray_remove(&tokens, num-2);
550                         continue;
551                 }
552
553                 if (num == 2 &&
554                     t2->tok == T_VAL &&
555                     t1->tok == T_RPAREN) {
556                         complain(&t1->place, "Excess right parenthesis");
557                         complain_fail();
558                         token_destroy(t1);
559                         tokenarray_remove(&tokens, num-1);
560                         continue;
561                 }
562
563                 if (num == 3 &&
564                     t3->tok == T_LPAREN &&
565                     t2->tok == T_VAL &&
566                     t1->tok == T_EOF) {
567                         complain(&t1->place, "Unclosed left parenthesis");
568                         complain_fail();
569                         token_destroy(t3);
570                         tokenarray_remove(&tokens, num-3);
571                         continue;
572                 }
573
574                 if (num == 2 &&
575                     t2->tok == T_VAL &&
576                     t1->tok == T_EOF) {
577                         /* accepting state */
578                         break;
579                 }
580
581                 if (num >= 1 &&
582                     t1->tok == T_EOF) {
583                         /* any other configuration at eof is an error */
584                         complain(&t1->place, "Parse error");
585                         complain_fail();
586                         break;
587                 }
588
589                 /* otherwise, wait for more input */
590                 break;
591         }
592 }
593
594 static
595 void
596 token(struct place *p, enum tokens tok, int val)
597 {
598         struct token *t;
599
600         t = token_create(p, tok, val);
601
602         tokenarray_add(&tokens, t, NULL);
603         tryreduce();
604 }
605
606 static
607 int
608 wordval(struct place *p, char *word)
609 {
610         unsigned long val;
611         char *t;
612
613         if (word[0] >= '0' && word[0] <= '9') {
614                 errno = 0;
615                 val = strtoul(word, &t, 0);
616                 if (errno) {
617                         complain(p, "Invalid integer constant");
618                         complain_fail();
619                         return 0;
620                 }
621                 while (*t == 'U' || *t == 'L') {
622                         t++;
623                 }
624                 if (*t != '\0') {
625                         complain(p, "Trailing garbage after integer constant");
626                         complain_fail();
627                         return 0;
628                 }
629                 if (val > INT_MAX) {
630                         complain(p, "Integer constant too large");
631                         complain_fail();
632                         return INT_MAX;
633                 }
634                 return val;
635         }
636
637         /* if it's a symbol, warn and substitute 0. */
638         if (warns.undef) {
639                 complain(p, "Warning: value of undefined symbol %s is 0",
640                          word);
641                 if (mode.werror) {
642                         complain_fail();
643                 }
644         }
645         debuglog(p, "Undefined symbol %s; substituting 0", word);
646         return 0;
647 }
648
649 static
650 bool
651 check_word(struct place *p, char *expr, size_t pos, size_t *len_ret)
652 {
653         size_t len;
654         int val;
655         char tmp;
656
657         if (!strchr(alnum, expr[pos])) {
658                 return false;
659         }
660         len = strspn(expr + pos, alnum);
661         tmp = expr[pos + len];
662         expr[pos + len] = '\0';
663         val = wordval(p, expr + pos);
664         expr[pos + len] = tmp;
665         token(p, T_VAL, val);
666         *len_ret = len;
667         return true;
668 }
669
670 static
671 bool
672 check_tokens_2(struct place *p, char *expr, size_t pos)
673 {
674         unsigned i;
675
676         for (i=0; i<num_tokens_2; i++) {
677                 if (expr[pos] == tokens_2[i].c1 &&
678                     expr[pos+1] == tokens_2[i].c2) {
679                         token(p, tokens_2[i].tok, 0);
680                         return true;
681                 }
682         }
683         return false;
684 }
685
686 static
687 bool
688 check_tokens_1(struct place *p, char *expr, size_t pos)
689 {
690         unsigned i;
691
692         for (i=0; i<num_tokens_1; i++) {
693                 if (expr[pos] == tokens_1[i].c1) {
694                         token(p, tokens_1[i].tok, 0);
695                         return true;
696                 }
697         }
698         return false;
699 }
700
701 static
702 void
703 tokenize(struct place *p, char *expr)
704 {
705         size_t pos, len;
706
707         pos = 0;
708         while (expr[pos] != '\0') {
709                 len = strspn(expr+pos, ws);
710                 pos += len;
711                 place_addcolumns(p, len);
712                 /* trailing whitespace is supposed to have been pruned */
713                 assert(expr[pos] != '\0');
714                 if (check_word(p, expr, pos, &len)) {
715                         pos += len;
716                         place_addcolumns(p, len);
717                         continue;
718                 }
719                 if (check_tokens_2(p, expr, pos)) {
720                         pos += 2;
721                         place_addcolumns(p, 2);
722                         continue;
723                 }
724                 if (check_tokens_1(p, expr, pos)) {
725                         pos++;
726                         place_addcolumns(p, 1);
727                         continue;
728                 }
729                 complain(p, "Invalid character %u in #if-expression",
730                          (unsigned char)expr[pos]);
731                 complain_fail();
732                 pos++;
733                 place_addcolumns(p, 1);
734         }
735         token(p, T_EOF, 0);
736 }
737
738 bool
739 eval(struct place *p, char *expr)
740 {
741         struct token *t1, *t2;
742         unsigned num;
743         bool result;
744
745 #ifdef DEBUG
746         fprintf(stderr, "eval: %s\n", expr);
747 #endif
748         debuglog(p, "eval: %s", expr);
749
750         tokenarray_init(&tokens);
751         tokenize(p, expr);
752
753         result = false;
754         num = tokenarray_num(&tokens);
755         if (num == 2) {
756                 t1 = tokenarray_get(&tokens, num-1);
757                 t2 = tokenarray_get(&tokens, num-2);
758                 if (t2->tok == T_VAL &&
759                     t1->tok == T_EOF) {
760                         result = t2->val != 0;
761                 }
762         }
763
764         tokenarray_destroyall(&tokens);
765         tokenarray_cleanup(&tokens);
766         return result;
767 }