/* * Copyright (c) 2004 - 2006, Nils R. Weller * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * * Expression parser */ #include "expr.h" #include #include #include #include "token.h" #include "error.h" #include "misc.h" #include "macros.h" #include "defs.h" #include "type.h" #include "subexpr.h" #include "typemap.h" #include "libnwcc.h" #include "n_libc.h" static int value_is_nonzero(struct tyval *cv) { int size; int i; unsigned char *uc; size = cross_get_sizeof_type(cv->type); for (uc = (unsigned char *)cv->value, i = 0; i < size; ++i, ++uc) { if (*uc != 0) { return 1; } } return 0; } #if 0 static void addr_add_sub(struct tyval *tv, struct expr *ex, struct type *ty, int op) { long val; int esize = backend->get_sizeof_elem_type(ty); static struct token optok; cross_do_conv(ex->const_value, TY_LONG); /* XXX what to do here? :( */ val = *(long *)ex->const_value->value * esize; if (op == TOK_OP_PLUS) { tv->address->diff += val; } else { tv->address->diff -= val; } } #endif static struct tyval do_eval_const_expr(struct expr *tree) { struct tyval ret; struct tyval tmp1; struct tyval tmp2; struct tyval posres; struct tyval negres; struct tyval *tv; struct tyval *left; struct tyval *right; unsigned char *uc; size_t size; size_t i; int rc = 0; int nullok; static struct tyval nulltv; ret = nulltv; tmp1 = nulltv; tmp2 = nulltv; if (tree->op == 0) { if (tree->data->is_expr) { ret = do_eval_const_expr(tree->data->is_expr); } else if (tree->data->meat != NULL) { struct token *t = tree->data->meat; if (t->type == TOK_STRING_LITERAL) { ret.str = t->data; ret.type = ret.str->ty; } else if (t->type == TOK_IDENTIFIER) { /* * All macro expansion has already been * done earlier, so this identifier is * not a macro, so it becomes the value * 0 */ ret.type = make_basic_type(TY_INT); ret.value = n_xmalloc(16); /* XXX */ *(int *)ret.value = 0; } else if (t->type == TOK_DEFINED) { struct token *tmp = t->data; int val; ret.type = make_basic_type(TY_INT); if (lookup_macro(tmp->data, 0) != NULL) { val = 1; } else { val = 0; } ret.value = n_xmalloc(16); /* XXX */ *(int *)ret.value = val; } else { /* Must be constant */ ret.type = make_basic_type(t->type); if (IS_FLOATING(ret.type->code)) { struct ty_float *fc; fc = t->data; ret.value = fc->num->value; } else { ret.value = t->data; } } ret.alloc = 0; } else if (tree->data->is_sizeof) { #ifndef PREPROCESSOR unsigned long size; struct vreg *vr; vr = tree->data->is_sizeof->data; ret.type = vr->type; size = vr->size; ret.value = vr->from_const->data; #endif } else { abort(); } /* Now apply all unary/postfix/prefix operators */ tv = &ret; /* * XXX TOK_OP_LNEG and such butchers token * data, which is bad for tokens that are * evaluated multiple times - thus the need * to copy * ^ is this comment still true?? */ tv->value = n_xmemdup(tv->value, 16); /* XXX */ for (i = 0; tree->data->operators[i] != NULL; ++i) { struct token *op; struct decl *d; int opval; op = tree->data->operators[i]; if (ret.value == NULL && ret.address == NULL && op->type != TOK_OP_CAST) { errorfl(op, "`%s' operator used with incorrect " "type", op->ascii); ret.type = NULL; return ret; } switch (op->type) { case TOK_OP_CAST: #ifndef PREPROCESSOR d = op->data; if (tv->is_nullptr_const) { if (d->dtype->tlist != NULL && d->dtype->tlist->type == TN_POINTER_TO && d->dtype->tlist->next == NULL && d->dtype->code == TY_VOID) { /* Remains nullptr */ ; } else { tv->is_nullptr_const = 0; } } if (d->dtype->tlist != NULL) { if (tree->data->meat && tree->data->meat->type == TOK_STRING_LITERAL) { ; } else { #if 0 do_conv(tv, TY_ULONG); /* XXX */ #endif if (ret.type->tlist == NULL) { cross_do_conv(tv, TY_ULONG); /* XXX */ tv->type = make_basic_type(TY_ULONG); } } } else { cross_do_conv(tv, d->dtype->code); } ret.type = d->dtype; #endif break; case TOK_ARRAY_OPEN: #ifdef PREPROCESSOR #else if (ret.address != NULL && (op=tree->data->operators[i+1]) != NULL && op->type == TOK_OPERATOR && *(int *)op->data == TOK_OP_ADDR) { /* make &foo[x] work */ struct expr *ex = tree->data->operators[i++]-> data; if (eval_const_expr(ex) != 0) { ret.type = NULL; return ret; } #if 0 /* XXXXXXXXX :/ */ addr_add_sub(&ret, ex, tv->type, TOK_OP_PLUS); #endif #if 0 do_conv(ex->const_value, TY_ULONG); ret.address->diff += *(unsigned long *)ex-> const_value->value * backend->get_sizeof_elem_type (tv->type); #endif #if 0 tv->type->tlist->type = TN_POINTER_TO; /* XXX */ #endif } else { errorfl(tree->data->operators[i], "Subscript operator used in" " constant expression"); ret.type = NULL; return ret; } #endif break; case TOK_OPERATOR: opval = *(int *)op->data; if (opval == TOK_OP_ADDR) { if (i == 0) { ; /* already taken care of */ } else { errorfl(op, "Parse error at `%s'", op->ascii); ret.type = NULL; return ret; } } else if (opval == TOK_OP_STRUMEMB) { unimpl(); } else if (opval == TOK_OP_STRUPMEMB) { unimpl(); } else if (opval == TOK_OP_LNEG || opval == TOK_OP_BNEG || opval == TOK_OP_UPLUS || opval == TOK_OP_UMINUS) { int oldtype = op->type; /* XXX needed? */ static struct tyval tytmp; op->type = opval; cross_exec_op(op, &tytmp, tv, NULL); op->type = oldtype; *tv = tytmp; } else { printf("unknown operator %d\n", opval); abort(); } break; case TOK_KEY_SIZEOF: case TOK_KEY_ALIGNOF: ; /* taken care of already */ break; default: printf("unknown operator %d\n", op->type); unimpl(); } } return ret; } tmp1 = do_eval_const_expr(tree->left); if (tree->op == TOK_OP_LAND) { int res = 0; if (value_is_nonzero(&tmp1)) { tmp2 = do_eval_const_expr(tree->right); if (value_is_nonzero(&tmp2)) { res = 1; } } ret.type = make_basic_type(TY_INT); ret.value = n_xmemdup(&res, sizeof res); goto out; } else if (tree->op == TOK_OP_LOR) { int res = 0; if (!value_is_nonzero(&tmp1)) { tmp2 = do_eval_const_expr(tree->right); if (value_is_nonzero(&tmp2)) { res = 1; } } else { res = 1; } ret.type = make_basic_type(TY_INT); ret.value = n_xmemdup(&res, sizeof res); goto out; } else if (tree->op != TOK_OP_COND) { /* XXX broken */ tmp2 = do_eval_const_expr(tree->right); nullok = 0; } else { /* Is TOK_OP_COND */ if (tree->right->op != TOK_OP_COND2) { errorfl(tree->right->tok, "Parse error - expected second part " "of conditional operator"); ret.type = NULL; return ret; } if (value_is_nonzero(&tmp1)) { tmp2 = do_eval_const_expr(tree->right->left); } else { tmp2 = do_eval_const_expr(tree->right->right); } if (tmp2.type == NULL) { return ret; } if (tmp2.type->code < TY_INT) { /* * XXX we also need some typechecking ... And * usual arithmetic conversions!!! E.g. * foo < bar? 0ll: 0; * ... should convert 0 to 0ll */ cross_do_conv(&tmp2, TY_INT); } ret.type = tmp2.type; ret.value = tmp2.value; goto out; } tv = &ret; left = &tmp1; right = &tmp2; if (tmp1.type == NULL || (!nullok && tmp2.type == NULL) || (!nullok && (rc = cross_convert_tyval(&tmp1, &tmp2)) != 0)) { ret.type = NULL; ret.value = NULL; if (rc != 0) { /* * XXX mm this is kludged, convert_tyval() shouldn't * return an error in the first place ;/ */ #if 0 /* XXX this sh ould never happen?!?! */ if (tmp1.address || tmp2.address) { if (tree->op == TOK_OP_PLUS) { static struct expr dum; /* Must be addr + integer */ if (tmp1.address == NULL) { dum.const_value = &tmp1; addr_add_sub(&tmp2, &dum, tmp2.type, TOK_OP_PLUS); return tmp2; } else if (tmp2.address == NULL) { dum.const_value = &tmp2; addr_add_sub(&tmp1, &dum, tmp1.type, TOK_OP_PLUS); return tmp1; } } else if (tree->op == TOK_OP_MINUS) { unimpl(); } } #endif errorfl(tree->tok, "Incompatible types in expression"); ret.type = NULL; return ret; } return ret; } if ((left->address == NULL && left->value == NULL) || (right->address == NULL && right->value == NULL)) { if (tree->op != TOK_OP_COND) { errorfl(tree->right->tok, "Incompatible types for " "operator"); ret.type = NULL; return ret; } } switch (tree->op) { case TOK_OP_PLUS: if (left->address || right->address) { unimpl(); } else { cross_exec_op(tree->tok, tv, left, right); } break; case TOK_OP_MINUS: if (left->address || right->address) { unimpl(); } else { cross_exec_op(tree->tok, tv, left, right); } break; case TOK_OP_MULTI: cross_exec_op(tree->tok, tv, left, right); break; case TOK_OP_DIVIDE: cross_exec_op(tree->tok, tv, left, right); break; case TOK_OP_SMALL: cross_exec_op(tree->tok, tv, left, right); break; case TOK_OP_GREAT: cross_exec_op(tree->tok, tv, left, right); break; case TOK_OP_GREATEQ: cross_exec_op(tree->tok, tv, left, right); break; case TOK_OP_SMALLEQ: cross_exec_op(tree->tok, tv, left, right); break; case TOK_OP_LEQU: cross_exec_op(tree->tok, tv, left, right); break; case TOK_OP_LNEQU: cross_exec_op(tree->tok, tv, left, right); break; case TOK_OP_MOD: cross_exec_op(tree->tok, tv, left, right); break; case TOK_OP_BAND: cross_exec_op(tree->tok, tv, left, right); break; case TOK_OP_BOR: cross_exec_op(tree->tok, tv, left, right); break; case TOK_OP_BXOR: cross_exec_op(tree->tok, tv, left, right); break; case TOK_OP_BSHL: cross_exec_op(tree->tok, tv, left, right); break; case TOK_OP_BSHR: cross_exec_op(tree->tok, tv, left, right); break; default: printf("Badness to the maximum %d\n", tree->op); abort(); } if (tree->op != TOK_OP_COND) { free(left->value); free(right->value); } out: return ret; } int eval_const_expr(struct expr *ex) { static struct tyval nulltv; struct tyval tv; struct tyval *dtv; if (ex->const_value != NULL) { /* already been evaluated */ return 0; } tv = nulltv; tv = do_eval_const_expr(ex); if (tv.type == NULL) { return -1; } else { rv_setrc_print(tv.value, tv.type->code, 0); dtv = n_xmalloc(sizeof tv); memcpy(dtv, &tv, sizeof tv); ex->const_value = dtv; } return 0; }