2 * Copyright (c) 2010 The NetBSD Foundation, Inc.
5 * This code is derived from software contributed to The NetBSD Foundation
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
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.
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.
55 * e1 < e2 | e1 <= e2 | e1 > e2 | e1 >= e2
58 * e1 * e2 | e1 / e2 | e1 % e2
64 T_EOF, /* end of input */
66 T_LPAREN, /* parens */
68 T_PIPEPIPE, /* operators */
96 { '|', '|', T_PIPEPIPE },
97 { '&', '&', T_AMPAMP },
99 { '!', '=', T_BANGEQ },
100 { '<', '=', T_LTEQ },
101 { '>', '=', T_GTEQ },
102 { '<', '<', T_LTLT },
103 { '>', '>', T_GTGT },
105 static const unsigned num_tokens_2 = HOWMANY(tokens_2);
107 static const struct {
128 static const unsigned num_tokens_1 = HOWMANY(tokens_1);
135 DECLARRAY(token, static UNUSED);
136 DEFARRAY(token, static);
138 static struct tokenarray tokens;
142 token_create(const struct place *p, enum tokens tok, int val)
146 t = domalloc(sizeof(*t));
155 token_destroy(struct token *t)
157 dofree(t, sizeof(*t));
160 DESTROYALL_ARRAY(token, );
170 fprintf(stderr, "tokens:");
171 num = tokenarray_num(&tokens);
172 for (i=0; i<num; i++) {
173 t = tokenarray_get(&tokens, i);
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;
203 fprintf(stderr, "\n");
209 isuop(enum tokens tok)
225 isbop(enum tokens tok)
245 isop(enum tokens tok)
261 getprec(enum tokens 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;
282 looser(enum tokens t1, enum tokens t2)
284 return getprec(t1) >= getprec(t2);
289 eval_uop(enum tokens op, int val)
292 case T_BANG: val = !val; break;
293 case T_TILDE: val = (int)~(unsigned)val; break;
294 case T_MINUS: val = -val; break;
296 default: assert(0); break;
303 eval_bop(struct place *p, int lv, enum tokens op, int rv)
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;
323 complain(p, "Negative bit-shift");
327 if ((unsigned)rv >= CHAR_BIT * sizeof(unsigned)) {
328 complain(p, "Bit-shift farther than type width");
333 return (int)((unsigned)lv << (unsigned)rv);
335 mask = ((unsigned)-1) << (CHAR_BIT * sizeof(unsigned) - rv);
336 lv = (int)(((unsigned)lv >> (unsigned)rv) | mask);
350 if (rv > 0 && lv > (INT_MAX - rv)) {
351 complain(p, "Integer overflow");
355 if (rv < 0 && lv < (INT_MIN - rv)) {
356 complain(p, "Integer underflow");
369 if (rv == -1 && lv == INT_MIN) {
373 complain(p, "Integer overflow");
380 if (lv == INT_MIN && rv < 0) {
381 complain(p, "Integer overflow");
385 if (lv == INT_MIN && rv > 0) {
386 complain(p, "Integer underflow");
394 if (lv > 0 && lv > INT_MAX / rv) {
395 complain(p, "Integer overflow");
399 if (lv < 0 && lv < INT_MIN / rv) {
400 complain(p, "Integer underflow");
408 complain(p, "Division by zero");
416 complain(p, "Modulus by zero");
422 default: assert(0); break;
432 struct token *t1, *t2, *t3, *t4, *t5, *t6;
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;
444 t3->tok == T_LPAREN &&
446 t1->tok == T_RPAREN) {
448 t2->place = t3->place;
451 tokenarray_remove(&tokens, num-1);
452 tokenarray_remove(&tokens, num-3);
457 (num == 2 || isop(t3->tok) || t3->tok == T_LPAREN) &&
461 t1->val = eval_uop(t2->tok, t1->val);
462 t1->place = t2->place;
464 tokenarray_remove(&tokens, num-2);
468 (num == 2 || isop(t3->tok) || t3->tok == T_LPAREN) &&
469 t2->tok != T_LPAREN && t2->tok != T_VAL &&
471 complain(&t2->place, "Invalid unary operator");
474 tokenarray_remove(&tokens, num-2);
479 t4 = (num >= 4) ? tokenarray_get(&tokens, num-4) : NULL;
485 /* binary operator */
486 if (looser(t1->tok, t3->tok)) {
487 t4->val = eval_bop(&t3->place,
488 t4->val, t3->tok, t2->val);
491 tokenarray_remove(&tokens, num-2);
492 tokenarray_remove(&tokens, num-3);
498 t5 = (num >= 5) ? tokenarray_get(&tokens, num-5) : NULL;
499 t6 = (num >= 6) ? tokenarray_get(&tokens, num-6) : NULL;
505 t3->tok == T_COLON &&
508 /* conditional expression */
509 t6->val = t6->val ? t4->val : t2->val;
514 tokenarray_remove(&tokens, num-2);
515 tokenarray_remove(&tokens, num-3);
516 tokenarray_remove(&tokens, num-4);
517 tokenarray_remove(&tokens, num-5);
522 t2->tok == T_LPAREN &&
523 t1->tok == T_RPAREN) {
524 complain(&t1->place, "Value expected within ()");
529 tokenarray_remove(&tokens, num-1);
536 complain(&t1->place, "Operator expected");
539 tokenarray_remove(&tokens, num-1);
546 complain(&t1->place, "Value expected after operator");
549 tokenarray_remove(&tokens, num-2);
555 t1->tok == T_RPAREN) {
556 complain(&t1->place, "Excess right parenthesis");
559 tokenarray_remove(&tokens, num-1);
564 t3->tok == T_LPAREN &&
567 complain(&t1->place, "Unclosed left parenthesis");
570 tokenarray_remove(&tokens, num-3);
577 /* accepting state */
583 /* any other configuration at eof is an error */
584 complain(&t1->place, "Parse error");
589 /* otherwise, wait for more input */
596 token(struct place *p, enum tokens tok, int val)
600 t = token_create(p, tok, val);
602 tokenarray_add(&tokens, t, NULL);
608 wordval(struct place *p, char *word)
613 if (word[0] >= '0' && word[0] <= '9') {
615 val = strtoul(word, &t, 0);
617 complain(p, "Invalid integer constant");
621 while (*t == 'U' || *t == 'L') {
625 complain(p, "Trailing garbage after integer constant");
630 complain(p, "Integer constant too large");
637 /* if it's a symbol, warn and substitute 0. */
639 complain(p, "Warning: value of undefined symbol %s is 0",
645 debuglog(p, "Undefined symbol %s; substituting 0", word);
651 check_word(struct place *p, char *expr, size_t pos, size_t *len_ret)
657 if (!strchr(alnum, expr[pos])) {
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);
672 check_tokens_2(struct place *p, char *expr, size_t pos)
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);
688 check_tokens_1(struct place *p, char *expr, size_t pos)
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);
703 tokenize(struct place *p, char *expr)
708 while (expr[pos] != '\0') {
709 len = strspn(expr+pos, ws);
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)) {
716 place_addcolumns(p, len);
719 if (check_tokens_2(p, expr, pos)) {
721 place_addcolumns(p, 2);
724 if (check_tokens_1(p, expr, pos)) {
726 place_addcolumns(p, 1);
729 complain(p, "Invalid character %u in #if-expression",
730 (unsigned char)expr[pos]);
733 place_addcolumns(p, 1);
739 eval(struct place *p, char *expr)
741 struct token *t1, *t2;
746 fprintf(stderr, "eval: %s\n", expr);
748 debuglog(p, "eval: %s", expr);
750 tokenarray_init(&tokens);
754 num = tokenarray_num(&tokens);
756 t1 = tokenarray_get(&tokens, num-1);
757 t2 = tokenarray_get(&tokens, num-2);
758 if (t2->tok == T_VAL &&
760 result = t2->val != 0;
764 tokenarray_destroyall(&tokens);
765 tokenarray_cleanup(&tokens);