/*
 * Copyright (c) 2004 - 2007, 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 "defs.h"
#include "decl.h"
#include "builtins.h"
#include "scope.h"
#include "type.h"
#include "icode.h"
#include "debug.h"
#include "cc1_main.h"
#include "subexpr.h"
#include "functions.h"
#include "analyze.h"
#include "symlist.h"
#include "backend.h"
#include "typemap.h"
#include "libnwcc.h"
#include "reg.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;
}

/*
 * XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
 * doesn't handle cross-compilation :(
 * address->diff should probably become a ``struct tyval'' and then we
 * need a nice set of utility functions to convert host values from/to
 * target-independent values. tyval is probably our best bet for
 * abstracting these things because exec_op() and such already use it
 * in the interface
 */
static void					
addr_add_sub(struct tyval *tv, struct expr *ex, struct type *ty, int op) {
	long		val;
	long		origval;
	int		esize = backend->get_sizeof_elem_type(ty);

	cross_do_conv(ex->const_value, TY_LONG);
	/* XXX what to do here? :( */
	ex->const_value->type->code = TY_LONG;
#if 0
	val = *(long *)ex->const_value->value * esize;
#endif

	val = (long)cross_to_host_size_t(ex->const_value);
	origval = val;
	val *= esize;

	if (tv->address != NULL) {
		if (op == TOK_OP_PLUS) {
			tv->address->diff += val;
		} else {
			tv->address->diff -= val;
		}
	} else {
		static struct tyval	src;
		static struct tyval	tmpval;
		static struct token	optok;
		struct type		*saved_type;
		int			opval = TOK_OP_PLUS;

		optok.type = TOK_OPERATOR;
		optok.data = &opval;
		src.value = ex->const_value->value;

		/*
		 * 08/19/07: Store (and later restore!) the value val
		 * as source. This was missing, so the array element
		 * calculation above was ignored
		 */
		cross_from_host_long(src.value, val, TY_LONG);
		src.type = make_basic_type(TY_LONG);

		/*
		 * 08/19/07: This wasn't working because, as a comment
		 * here rightly said :-), we have to use a temporary
		 * tyval as target instead of using tv as target and
		 * destination 
		 */
		saved_type = tv->type;
		tv->type = src.type;
		cross_exec_op(&optok, /*tv*/&tmpval, tv, &src); 
		cross_from_host_long(src.value, origval, TY_LONG);
		tv->value = tmpval.value;
		tv->type = saved_type;
	}
}


static struct tyval
do_eval_const_expr(struct expr *tree, int extype) {
	struct tyval		ret;
	struct tyval		tmp1;
	struct tyval		tmp2;
	struct tyval		*tv;
	struct tyval		*left;
	struct tyval		*right;
	size_t			i;
	int			rc = 0;
	int			nullok = 1;
	static struct tyval	nulltv;
	struct vreg		*builtinvr = NULL;

	ret = nulltv;
	tmp1 = nulltv;
	tmp2 = nulltv; 
	
	if (tree->op == 0) {
		static struct vreg	structvr;
		struct vreg	*tmpvr;
		struct decl	*dp;

#if 0
		if (extype == EXPR_OPTCONSTINIT
			&& (tree->data->operators[0] != NULL
			&& *(int *)tree->data->operators[0]->data
			!= TOK_OP_ADDR)) {
			/*
			 * Assume this is probably not const 
			 */
			ret.type = NULL;
			ret.not_constant = 1;
			return ret;
		}
#endif
			
		if (tree->data->is_expr) {	
			ret = do_eval_const_expr(tree->data->is_expr, extype);
			if (ret.type == NULL) {
				return ret;
			}	
		} 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) {		
				struct decl	*d;

				if (tree->data->operators[0] != NULL
					&& tree->data->operators[0]->type ==
					TOK_PAREN_OPEN) {
					struct fcall_data	*fd;

					fd = tree->data->operators[0]->data;
					if (fd->builtin != NULL
						&& fd->builtin->type ==
						BUILTIN_OFFSETOF) {
						builtinvr = fd->builtin->
							builtin->
							toicode(fd, NULL);
						ret.type = builtinvr->type;
						ret.value = builtinvr->
							from_const->data;
								
						goto builtin_done;
					}
				}	
				if ((d = access_symbol(curscope,
					t->data, 1)) == NULL) {
					/*
					 * This may be a call to an undeclared
					 * function if this is a VLA size
					 * expression or variable initializer
					 */
					if (extype == EXPR_OPTCONSTINIT
					|| extype == EXPR_OPTCONSTARRAYSIZE) {
						ret.not_constant = 1;
						ret.type = NULL;
					} else {	
						errorfl(t,
						"Undeclared identifier `%s'",
						t->ascii);
						ret.type = ret.value = NULL;
					}
					return ret;
				}	
				if (d->dtype->storage != TOK_KEY_STATIC
					&& d->dtype->storage != TOK_KEY_EXTERN
					&& d->dtype->tenum == NULL
					&& (d->dtype->tlist == NULL
					|| d->dtype->tlist->type
						!= TN_FUNCTION)) {
					/*
					 * XXX seems totally bogus.. only
					 * functions should be allowed at all for
					 * *values*???
					 */
					if (extype == EXPR_OPTCONSTINIT
						|| extype == EXPR_OPTCONSTARRAYSIZE) {
						if (extype == EXPR_OPTCONSTINIT) {
							warningfl(t, "Variable "
							"initializers are not "
							"allowed in ISO C90");
						} else {
							warningfl(t, "Variable-"
							"size arrays are not "
							"allowed in ISO C90");
						}	
						ret.not_constant = 1;
					} else {	
						errorfl(t, "Use of non-static "
						"identifier `%s' in constant"
						" expression", d->dtype->name);
					}
					ret.type = ret.value = NULL;
					return ret;
				}
				ret.type = d->dtype;
				if (d->dtype->tlist == NULL
					&& d->dtype->tenum != NULL) {

					/*
					 * The value is freed after use.  This is
					 * OK for tokens, but not for enum
					 * constants, whose values may be needed
					 * later - possibly in the same
					 * expression. So create a copy
					 */
					ret.value = n_xmemdup(
						d->vreg->from_const->data,
						sizeof(int));
				} else {
					int	is_addr = 1 /*0*/;

					/*
					 * An identifier used in a constant
					 * expression may only be used for
					 * an address constant
					 */
#if 0
					if (tree->data->operators[0] != NULL
						&& tree->data->operators[0]
						->type
						== TOK_OPERATOR) {
						if (*(int *)tree->data->
							operators[0]->data
							== TOK_OP_ADDR) {
							is_addr = 1;
						}	
					}
#endif

					if (!is_addr
						&& (d->dtype->tlist == NULL
						|| d->dtype->tlist->type
						== TN_POINTER_TO)) {
						unimpl();
					} else {
						/* Address constant */
#if 0
						if (is_addr) {
							ret.type =
								addrofify_type
								(ret.type);
						}
#endif

						ret.address =
							n_xmalloc(sizeof *
							ret.address);
						ret.address->dec = d;
						ret.address->diff = 0;
					}
				}
			} else {
				/* Must be constant */
				ret.type = make_basic_type(t->type);
				ret.type = n_xmemdup(ret.type,
					sizeof *ret.type);

				if (t->type == TOK_COMP_LITERAL) {
					struct comp_literal	*lit;

					lit = t->data;
					if (tree->data->operators[0] != NULL) {
						/*
						 * Don't handle things like
						 *
						 *   static struct *foo =
						 *     &(struct foo) {...};
						 *
						 * just yet
						 */
						unimpl();
					}
					ret.static_init = lit->init; 
				} else if (IS_FLOATING(ret.type->code)) {
					struct ty_float	*fc;

					fc = t->data;
					ret.value = fc->num->value;
				} else {
					ret.value = t->data;
				}
				ret.is_nullptr_const =
					is_nullptr_const(t, ret.type);
			}
builtin_done:			
			ret.alloc = 0;
		} else if (tree->data->is_sizeof) {
			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;
		} else {
			abort();
		}

		/* Now apply all unary/postfix/prefix operators */
		tv = &ret;
		if (builtinvr != NULL) {
			/*
			 * Meat of subexpression is builtin function -
			 * ignore first (function call) operator
			 */
			i = 1;
		} else {
			i = 0;
		}	
		for (/*i = 0*/; tree->data->operators[i] != NULL; ++i) {
			struct token	*op;
			struct decl	*d;
			struct token	*ident;
			int		opval;

			op = tree->data->operators[i];
			if (ret.value == NULL
				&& ret.address == NULL
				&& op->type != TOK_OP_CAST
				&& (op->type != TOK_OPERATOR
				|| *(int *)op->data !=TOK_OP_ADDR)
				&& (ret.type->code != TY_STRUCT
				|| (op->type != TOK_OP_STRUMEMB
					&& op->type != TOK_OP_STRUPMEMB))) {
				errorfl(op,
					"`%s' operator used with incorrect "
					"type", op->ascii);
				ret.type = NULL;
				return ret;
			}

			switch (op->type) {
			case TOK_OP_CAST:	
				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);
						tv->type = n_xmemdup(tv->type,
							sizeof *tv->type);
						}	
					}
					ret.type = d->dtype;
				} else {
					if (tv->type->tlist != NULL) {
						/*
						 * Very dangerous! A pointer
						 * cast to an integer as a
						 * *constant* expression! This
						 * only makes sense if source
						 * and target are the same
						 * size. We have to support
						 * this for programs like 
						 * xterm
						 */
						size_t	dest_size = 
					backend->get_sizeof_type(d->dtype,0);
						size_t	src_size =
					backend->get_ptr_size();		

						if (src_size == dest_size) {
							warningfl(op,
			"Nonportable bogus cast of pointer to integer in "
			"constant expression");
						} else {
							if (extype == EXPR_OPTCONSTINIT
							|| extype == EXPR_OPTCONSTARRAYSIZE) {
							if (extype == EXPR_OPTCONSTINIT) {
							warningfl(op, "Variable "
							"initializers are not "
							"allowed in ISO C90");
							} else {
							warningfl(op, "Variable-"
							"size arrays are not "
							"allowed in ISO C90");
							}	
							ret.not_constant = 1;
							ret.type = NULL;
							return ret;
							} else {	
								errorfl(op,
			"Invalid bogus cast of pointer to integer in "
			"constant expression");
							}
						}	

						/*
						 * BEWARE!!! We keep the type as
						 * ``pointer'' because the backend
						 * otherwise gets confused and
						 * thinks this really is a long. Hm
						 *
						 * 08/18/07 If it is an array, we
						 * change the type to pointer
						 * because otherwise the backends
						 * do not understand this kludged
						 * initializer
						 */
						if (tv->type->tlist->type ==
							TN_ARRAY_OF) {
							tv->type =
								dup_type(tv->type);
							tv->type->tlist->type =
								TN_POINTER_TO;
						}
						tv->type = d->dtype;
					} else {
						cross_do_conv(tv,
							d->dtype->code);
						ret.type = d->dtype;
					}
				}	
				if (ret.type->code == TY_ENUM
					&& ret.type->tlist == NULL) {
					/* XXX is this really desirable? */
					ret.type = n_xmemdup(make_basic_type(TY_INT),
						sizeof(struct type));
				}
				break;
			case TOK_ARRAY_OPEN:
#if 0
				if (ret.address != NULL
					&& (op=tree->data->operators[i+1])
						!= NULL
					&& op->type == TOK_OPERATOR
					&& *(int *)op->data == TOK_OP_ADDR) {
#endif
				{
					/* make &foo[x] work */
					struct expr	*ex
						= tree->data->operators[ i /*++ */]->
						data;

					if (eval_const_expr(ex, extype,
						&ret.not_constant) != 0) {
						ret.type = NULL;
						return ret; 
					}
					addr_add_sub(&ret, ex, tv->type,
						TOK_OP_PLUS);
					ret.type = dup_type(ret.type);
					ret.type->tlist = ret.type->tlist->next;
				}	

#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
#if 0
				} else {	
					errorfl(tree->data->operators[i],
						"Subscript operator used in"
						" constant expression");
					ret.type = NULL;
					return ret;
				}
#endif
				break;
			case TOK_PAREN_OPEN:
				/* Must be function call operator */
				if (extype == EXPR_OPTCONSTINIT
					|| extype == EXPR_OPTCONSTARRAYSIZE) {
					if (extype == EXPR_OPTCONSTINIT) {
						warningfl(op, "Variable "
						"initializers are not "
						"allowed in ISO C90");
					} else {
						warningfl(op, "Variable-"
						"size arrays are not "
						"allowed in ISO C90");
					}	
					ret.not_constant = 1;
				} else {	
					errorfl(op, "Invalid function call "
					"in constant expression");
				}
				ret.type = NULL;
				return ret;
				break;
			case TOK_OP_STRUMEMB: /* XXX !??why not TOK_OPERATOR */
			case TOK_OP_STRUPMEMB:
				if (ret.type == NULL
					|| ((ret.type->code != TY_STRUCT
					&& ret.type->code != TY_UNION)
					 /* || ret.type->tlist != NULL */  )) {
					errorfl(tree->data->operators[i],
						"Incorrect type for "
						"operator `%s'",
						tree->data->operators[i]->ascii);
					ret.type = NULL;
					return ret;
				}

				ident = op->data;
				if ((dp = access_symbol(ret.type->tstruc->
					scope, ident->data, 0)) == NULL) {
					errorfl(tree->data->operators[i],
						"Unknown structure member "
						"`%s'", ident->data);
					ret.type = NULL;
					return ret;
				}	
				if (op->type == TOK_OP_STRUMEMB) {
					/* . operator */
					tmpvr = vreg_alloc(dp, NULL, NULL, NULL);
				} else {
					/* -> operator */
					tmpvr = vreg_alloc(NULL, NULL, NULL,
						dp->dtype);
				}
#if 0
				tmpvr->memberdecl = dp;
				tmpvr->parent = structvr;
#endif

				/*
				 * XXX this is quite ad-hocly kludged to make
				 * nwcc work with the gtar source... it does
				 * not understand named address constants
				 * yet and is generally incomplete
				 */
				if (ret.value) {
					static struct vreg	parentvr;

					parentvr.type = ret.type;
					structvr.parent = &parentvr; 
					structvr.memberdecl = dp;	
					*(long *)ret.value += calc_offsets(&structvr);
				} else if (ret.address != NULL) {
					static struct vreg	parentvr;

					parentvr.type = ret.type;
					structvr.parent = &parentvr; 
					structvr.memberdecl = dp;	
					ret.address->diff +=
						calc_offsets(&structvr);
				} else {
					unimpl();
				}
				ret.type = dp->dtype;
				break;
			case TOK_OPERATOR:
				opval = *(int *)op->data;
				if (opval == TOK_OP_ADDR) {
					if (i == 0) {
						; /* already taken care of */
					} else {
#if 0 
						errorfl(op,
							"Parse error at `%s'",
							op->ascii);
						ret.type = NULL;
						return ret;
#endif
					}	
#if 0
					if (ret.type->tlist == NULL) {
#endif
						/* XXX this sucks */
						ret.type =
							addrofify_type
							(ret.type);
#if 0
					}
#endif
#if 0
				} else if (opval == TOK_OP_STRUMEMB) {
					unimpl();
				} else if (opval == TOK_OP_STRUPMEMB) {
					unimpl();
#endif
				} 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, extype);
	if (tmp1.type == NULL) {
		return tmp1;
	}	

	if (tree->op == TOK_OP_LAND) {
		int	res = 0;

		if (value_is_nonzero(&tmp1)) {
			tmp2 = do_eval_const_expr(tree->right, extype);
			if (value_is_nonzero(&tmp2)) {
				res = 1;
			}
		}
		ret.type = make_basic_type(TY_INT);
		ret.type = n_xmemdup(ret.type, sizeof *ret.type);
		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, extype);
			if (value_is_nonzero(&tmp2)) {
				res = 1;
			}
		} else {
			res = 1;
		}
		ret.type = make_basic_type(TY_INT);
		ret.type = n_xmemdup(ret.type, sizeof *ret.type);
		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, extype);
		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, extype);
		} else {
			tmp2 = do_eval_const_expr(tree->right->right, extype);
		}
		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;
		ret.not_constant = tmp1.not_constant || tmp2.not_constant;
		if (rc != 0) {
			/*
			 * XXX mm this is kludged, convert_tyval() shouldn't
			 * return an error in the first place ;/
			 */
			if (tmp1.address || tmp2.address) {
				if (tree->op == TOK_OP_PLUS
					|| tree->op == TOK_OP_MINUS) {
					static struct expr	dum;
					int			op = tree->op;

					/* Must be addr + integer */
					if (tmp1.address == NULL) {
						dum.const_value = &tmp1;
						addr_add_sub(&tmp2, &dum,
							tmp2.type,
							op);
						return tmp2;
					} else if (tmp2.address == NULL) {
						dum.const_value = &tmp2;
						addr_add_sub(&tmp1, &dum,
							tmp1.type,
							op);
						return tmp1;
					}
				}
			}
			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, int extype, int *not_constant) {
	struct tyval	tv;
	struct tyval	*dtv;

	if (ex->const_value != NULL) {
		/* already been evaluated */
		return 0;
	}	
	tv = do_eval_const_expr(ex, extype);
	if (tv.type == NULL) {
		if (not_constant != NULL) {
			*not_constant = tv.not_constant;
		}	
		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