/*
* 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 <stdlib.h>
#include <string.h>
#include <limits.h>
#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;
}
syntax highlighted by Code2HTML, v. 0.9.1