/*
 * 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