/*
 * Copyright (c) 2005 - 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.
 *
 * Parsing of, and icode generation for, sub-expressions
 */
#include "expr.h"
#include "subexpr.h"
#include <stdlib.h>
#include <string.h>
#include "token.h"
#include "error.h"
#include "misc.h"
#include "defs.h"
#include "decl.h"
#include "scope.h"
#include "type.h"
#include "icode.h"
#include "symlist.h"
#include "cc1_main.h"
#include "debug.h"
#include "functions.h"
#include "backend.h"
#include "reg.h"
#include "x87_nonsense.h"
#include "builtins.h"
#include "n_libc.h"

struct context {
	struct type		curtype;
	struct decl		*var_lvalue;
	struct vreg		*curitem;
	struct vreg		*load;
	int			is_lvalue;
	int			indir;
};

static struct s_expr *
alloc_s_expr(void) {
	struct s_expr	*ret = n_xmalloc(sizeof *ret);
	static struct s_expr nullop;
	*ret = nullop;
	return ret;
}



static int
ambig_to_unary(struct token *t, int is_prefix) {
	int	op = *(int *)t->data;

	switch (op) {
	case TOK_OP_AMB_PLUS:
		if (is_prefix) op = TOK_OP_UPLUS;
		break;
	case TOK_OP_AMB_MINUS:
		if (is_prefix) op = TOK_OP_UMINUS;
		break;
	case TOK_OP_AMB_MULTI:
		if (is_prefix) op = TOK_OP_DEREF;
		break;
	case TOK_OP_AMB_BAND:
		if (is_prefix) op = TOK_OP_ADDR;
		break;
	case TOK_OP_AMB_INCR:
		if (is_prefix) op = TOK_OP_INCPRE;
		else op = TOK_OP_INCPOST;
		break;
	case TOK_OP_AMB_DECR:
		if (is_prefix) op = TOK_OP_DECPRE;
		else op = TOK_OP_DECPOST;
		break;
	default:
		/* XXX handle labels!  foo:  */
		;
		break;
	}

	if (is_prefix && !IS_UNARY(op)) {
		/*
		 * foo + bar   is ok
		 * / foo       is not
		 */
		errorfl(t, "Invalid use of operator `%s'", t->ascii);
		return -1;
	}
	return op;
}



static int
check_memb_select(struct type *curtype, struct token *op) { 
	char	*p;

	if (curtype->code == 0) {
		errorfl(op,
			"Invalid use of `%s' operator", op->ascii);
		return -1;
	}
	if (curtype->tstruc == NULL) {
		errorfl(op,
	"`%s' operator applied to non-union/structure type",
			op->ascii);
		return -1;
	}
		
	if (op->type == TOK_OP_STRUMEMB) {
		/* . requires plain struct/union type */
		if (curtype->tlist != NULL) {
			p = curtype->code == TY_UNION? "union": "struct";
			if (curtype->tstruc->tag != NULL) {
				errorfl(op,
				"`.' operator applied to something "
				"that is not of type `%s %s'",
					p,
					curtype->tstruc->tag);
			} else {
				errorfl(op,
				"`.' operator applied to something "
				"that is not of type `%s'", p);
			}
			return -1;
		}	
	} else {
		/*
	 	 * -> requires *single* pointer (or
		 * array, since array types decay
		 * into pointers in expressions)
		 * struct/union 
		 */
		if (curtype->tlist == NULL
			|| curtype->tlist->next != NULL
			|| curtype->tlist->tfunc != NULL) {
			p = curtype->code == TY_UNION? "union": "struct";
			if (curtype->tstruc->tag != NULL) {
				errorfl(op,
					"`->' operator applied to "
					"something that is not of "
					"type `%s %s *'",
					p,
					curtype->tstruc->tag);
			} else {
				errorfl(op,
				"`->' operator applied to "
					"something that is not of "
					"type `pointer to struct/union'");
			}
			return -1;
		}
	}
	return 0;
}	


static void
print_nomemb(struct type *curtype, struct token *tok, const char *member) {
	char	*p;

	p = curtype->code == TY_UNION? "Union": "Structure";
	if (curtype->tstruc->tag != NULL) {
		errorfl(tok,
			"%s type `%s' has no member named `%s'",
			p, curtype->tstruc->tag, member);
	} else {
		errorfl(tok, "%s type has no member named `%s'",
			p, member); 
	}
}	


/*
 * XXX this stuff ends up handling unions and structs identically, thus
 * deferring all the work to the backend :(
 * I've tried removing the union details at this point and patching up
 * all member stack addressing info in the backend as necessary ... But
 * it just never works. Would be cool to have base addresses and offsets
 * such that the var_backed/from_const/stack_addr/from_ptr mess becomes
 * say one ``struct address'' and then one could do
 * struct address *member = addr_offset(base, nbytes);
 * or something like that. Deferring all of this offset addressing
 * nonsense is terrible to the maximum :(
 */
static int 
do_struct_member(
	struct token *tok,
	struct context *context,
	struct icode_list *il,
	int eval) {

	struct decl		*dp;
	struct decl		*uniondp = NULL;
	struct type		*curtype = &context->curtype;
	struct token		*ident = tok->data;
	struct vreg		*vr = NULL;
	int 			is_ptr;
	int			is_struct;
	int			is_anon_union = 0;

	is_ptr = tok->type == TOK_OP_STRUPMEMB;
	is_struct = curtype->code == TY_STRUCT;

	if (check_memb_select(curtype,
		tok) == -1) {
		return -1;
	}	
	if (curtype->tstruc->incomplete) {
		errorfl(tok,
			"Cannot access members of incomplete structure type");
		return -1;
	}	
	++curtype->tstruc->references;


	dp = access_symbol(curtype->tstruc->scope, ident->data, 0);
	if (dp == NULL) {
		/*
		 * Member not found. This may be an anonymous union member!
		 */	
		struct sym_entry	*se;
		
		for (se = curtype->tstruc->scope->slist;
			se != NULL;
			se = se->next) {
			struct type	*ty = se->dec->dtype;

			if (ty->code == TY_UNION
				&& ty->tlist == NULL
				&& ty->name == NULL) {
				/*
				 * This is an anonymous union! But does it
				 * contain the identifier we are looing for?
				 */
				dp = access_symbol(se->dec->dtype->tstruc->scope,
					ident->data, 0);	
				if (dp != NULL) {
					uniondp = se->dec;
					break;
				}
			}
		}
		if (dp == NULL) {
			print_nomemb(curtype, tok, ident->data);
			return -1;
		}
		is_anon_union = 1;
	}

	copy_type(curtype, dp->dtype, 0);
	curtype->storage = 0;
	if (eval) {
		if (is_ptr) {
			vr = vreg_alloc(NULL, NULL, NULL, dp->dtype);
			/* Avoid indirection chain */
#if 0
			if (context->curitem->from_ptr) {
				vreg_anonymify(&context->curitem,
					NULL, NULL, il);
			}
#endif
			/*
			 * XXX 07/09/07: The UNCONDITIONAL anonymify and
			 * vreg_disconnect are new. Without them, map_pregs
			 * in backend.c trashed a shared vreg register
			 * reference when assigning NULL to pregs[0]!
			 */
			vreg_anonymify(&context->curitem, NULL, NULL, il);
			context->curitem = vreg_disconnect(context->curitem);
			if (!is_anon_union) {
				vr->from_ptr = context->curitem;
			}
		} else {
			vr = vreg_alloc(dp, NULL, NULL, dp->dtype);
		}
		
		vr->memberdecl = dp;
		if (is_anon_union) {
			vr->parent = vreg_alloc(NULL, NULL, NULL,
				uniondp->dtype);
			vr->parent->memberdecl = uniondp;
			if (is_ptr) {
				vr->parent->from_ptr = context->curitem;
			}	
			vr->parent->parent = context->curitem;
		} else {	
			vr->parent = context->curitem;
		}
	
		context->curitem = vr;
	} else {
		vr = vreg_alloc(NULL, NULL, NULL, NULL);
		vreg_set_new_type(vr, dp->dtype);
		context->curitem = vr;
	}
	if (!is_ptr) {
		context->var_lvalue = dp;
	} else {
		context->var_lvalue = NULL;
	}	
	context->is_lvalue = 1;
	if (context->load == NULL) {
		context->load = vr;
	}
	return 0;
}

static int
do_subscript(struct context *context, struct token *tok,
	struct icode_list *il, int eval) {
	struct expr		*ex;
	struct vreg		*exvr;
	struct type		*curtype = &context->curtype;

	if (curtype->code == 0) {
		errorfl(tok, "Parse error at `['");
		return -1;
	}	
	ex = tok->data;

	exvr = expr_to_icode(ex, NULL, il, 0, eval);
	if (exvr == NULL) {
		return -1;
	}	

	if (exvr->type->tlist == NULL
		&& curtype->tlist == NULL) {
		errorfl(tok,
			"Array subscript operator applied to non-pointer type");
		return -1;
	} else if (exvr->type->tlist != NULL
		&& curtype->tlist != NULL) {
		errorfl(tok,
			"Index with bad type for array subscript operator");
		return -1;
	} else if (curtype->code == TY_VOID) {
		if (curtype->tlist != NULL
			&& curtype->tlist->next == NULL) {
			errorfl(tok, "Cannot subscript void pointers");
			return -1;
		}
	} else if ((exvr->type->tlist == NULL
		&& !is_integral_type(exvr->type))
		|| (curtype->tlist == NULL
			&& !is_integral_type(curtype))) {
		errorfl(tok, "Subscript not of integral type");
		return -1;
	}	

	if (eval) {
#if 0
		vreg_faultin(NULL, NULL, exvr, il, 0);
#endif
		/* XXX doesn't work for num[ptr] */
		if (promote(&exvr, NULL, 0, NULL, il, 1) == NULL) {
			return -1;
		}	
		vreg_faultin_protected(exvr, NULL, NULL,
				context->curitem, il, 0);
		if (context->curitem->type->tlist->type == TN_ARRAY_OF) {
			context->curitem = vreg_disconnect(context->curitem);
			context->curitem->type = n_xmemdup(context->curitem->type,
				sizeof(struct type));
			copy_tlist(&context->curitem->type->tlist,
				context->curitem->type->tlist);
			context->curitem->type->tlist->type = TN_POINTER_TO;
			context->curitem->size =
				backend->get_sizeof_type(context->curitem->type, NULL);
		}
		do_add_sub(&context->curitem, &exvr, TOK_OP_PLUS, NULL, il, eval);
		free_pregs_vreg(exvr, il, 0, 0);
	}
	copy_type(&context->curtype, context->curitem->type, 0);
	context->curtype.storage = 0;
	copy_tlist(&context->curtype.tlist, context->curtype.tlist->next);
	context->is_lvalue = 1;
	context->var_lvalue = NULL;

#if 0
	if (context->curitem->from_ptr && eval) {
#endif
		/* Avoid chain of indirection */
#if 0
		vreg_anonymify(&context->curitem, NULL, NULL, il);
#endif
		context->curitem = vreg_disconnect(context->curitem);
#if 0
		if (context->curitem->type->tlist
			&& context->curitem->type->tlist->type == TN_ARRAY_OF) {
			context->curitem->type = addrofify_type(context->curitem->type); 
		}
#endif
#if 0
	}
#endif
	context->curitem
			= vreg_alloc(NULL, NULL, context->curitem, NULL);
	return 0;
}

struct fcall_data *
alloc_fcall_data(void) {
	struct fcall_data	*ret = n_xmalloc(sizeof *ret);
	static struct fcall_data	nullfdat;
	*ret = nullfdat;
	return ret;
}		


static struct fcall_data *
do_func_call(struct token *meat, struct token **tok, int eval) {
	struct expr		*ex;
	struct expr		*args = NULL;
	struct expr		*argstail = NULL;
	struct token		*t = *tok;
	struct fcall_data	*fcall = NULL;
	int			nargs = 0;

	if (next_token(&t) != 0) {
		return NULL;
	}

	if (meat && meat->type == TOK_IDENTIFIER) {
		size_t	len  = sizeof "__builtin_" - 1;

		if (strncmp(meat->data, "__builtin_", len) == 0) {
			*tok = t;
			return get_builtin(tok, meat); 
		} else if (*(char *)meat->data == 'a'
			&& strcmp(meat->data, "alloca") == 0) {
			/*
			 * alloca() support SUCKS! Linux/BSD libraries
			 * assume __builtin_alloca() if __GNUC__ is
			 * defined. However, if we don't define it, it
			 * declares alloca() without prodiving a
			 * definition! So we have to catch that too
			 * because we occasionally have to undefine
			 * __GNUC__ (gcc itself cannot build programs
			 * using alloca() if we explicitly pass
			 * -U__GNUC__.)
			 */
			*tok = t;
			return get_builtin(tok, meat);
		 } 
	}
	
	for (;;) {
		if (t->type == TOK_PAREN_CLOSE) {
			/* Function call ends here */
#ifdef DEBUG3
			printf("Parsed function call with %d arguments\n",
				nargs);
#endif
			if (eval) {
				fcall = alloc_fcall_data();
				fcall->args = args;
				fcall->nargs = nargs;
			}	
			break;
		}

		ex = parse_expr(&t, TOK_OP_COMMA, TOK_PAREN_CLOSE, 0, 1);

		if (ex != NULL) {
			++nargs;
			append_expr(&args, &argstail, ex);
			if (t->type != TOK_PAREN_CLOSE) {
				/* Must be comma */
				if (next_token(&t) != 0) {
					return NULL;
				}
			}
		} else {
			return NULL;
		}
	}

	*tok = t;
	return fcall;
}

/*
 * Parses sizeof or alignof operator because they act exactly the same,
 * including the type of the result
 */
static int
do_sizeof(
	struct token *t, 
	struct token **endp,
	struct token **is_sizeof,
	int extype,
	int delim, int delim2) {

	struct token	*newconst = NULL;
	struct type	*restype;
	int		is_alignof = t->type == TOK_KEY_ALIGNOF;
	int		is_vla = 0;
	struct s_expr	*subex = NULL;


	*is_sizeof = t;
	if (next_token(&t) != 0) {
		return -1;
	}
	if (t->type == TOK_PAREN_OPEN
		&& t->next
		&& IS_TYPE(t->next)) {
		struct decl	**d;
		
		/*
		 * sizeof(type-name)
		 */
		(void) next_token(&t);
			
		d = parse_decl(&t, DECL_CAST);
		if (d == NULL) {
			return -1;
		}	
		restype = d[0]->dtype;
		if (restype->is_vla) {
			is_vla = 1;
			(*is_sizeof)->type = TOK_SIZEOF_VLA_EXPR;
			(*is_sizeof)->data = d[0]->dtype;
		}
		free(d);

		/* Skip ``)'' */
		if (next_token(&t) != 0) {
			return -1;
		}
	} else {
		/* sizeof sub-expr */
		subex = get_sub_expr(&t, delim, delim2, 0);
		if (subex == NULL) {
			return -1;
		}

		/* Don't evaluate! */
		if (s_expr_to_icode(subex, NULL, NULL, 0) == NULL) {
			return -1;
		}	
		if (subex->type == NULL) {
			return -1;
		}	
		restype = subex->type;
		if (restype->is_vla) {
			is_vla = 1;
			(*is_sizeof)->type = TOK_SIZEOF_VLA_EXPR;
			(*is_sizeof)->data = subex;
		}
	}
	*endp = t;

	if (restype->tlist
		&& restype->tlist->type == TN_FUNCTION) {	
		errorfl(*is_sizeof,
			"Cannot take size of function");
		return -1;
	}
	if (restype->incomplete
		|| (restype->tstruc != NULL	
		&& restype->tstruc->incomplete
		&& is_basic_agg_type(restype))) {
		errorfl(*is_sizeof,
			"Cannot take size of incomplete type");
		return -1;
	}

	if (!is_vla) {
		newconst = const_from_type(restype, is_alignof, extype);
		(*is_sizeof)->data = vreg_alloc(NULL, newconst, NULL, NULL);
	}	
	return 0;
}


static struct icode_instr *
do_identifier(
	struct context *context,
	struct token *t,
	int extype,
	struct decl **is_func_call,
	int *err,
	int eval) {

	struct decl		*dec;
	struct type		*curtype = &context->curtype;
	struct icode_instr	*ret = NULL;
	struct vreg		*vr;
	int			is_implicit = 0;

	dec = access_symbol(curscope, t->data, 1);
	*err = 1;

	if (is_func_call) {
		if ((*is_func_call = dec) == NULL) {
			/*
			 * Call to an undeclared (implicitly
			 * int in C89) or builtin function.
		 	 */
			int	is_builtin = 0;

			if (strcmp(t->data, "malloc") == 0
				|| strcmp(t->data, "realloc") == 0
				|| strcmp(t->data, "calloc") == 0) {
				warningfl(t,
					"Incorrect call to `%s' without "
					"declaration, please `#include "
					"<stdlib.h>'", t->data);
			} else if (strncmp(t->data, "__builtin_",
				sizeof "__builtin_" - 1) != 0) {	
				warningfl(t,
			"Call to `%s' without declaration (illegal in C99)",
					t->data);
			} else {
				is_builtin = 1;
			}	

			if (!is_builtin) {
				dec = put_implicit(t->data);
				is_implicit = 1;
			} else {
				*err = 0;
				return NULL;
			}	
			*is_func_call = dec;
		} else if (0 /* is not really a function!~*/) {
			/* XXX handle */
		} else if (dec->dtype->implicit) {
			is_implicit = 1;
		}	
		*err = 0;
	}

	if (dec == NULL) {
		if (/*(dec=lookup_undeclared(curscope,t->data,1))==NULL*/1) {
			errorfl(t, "Undeclared identifier `%s'",
				(char *)t->data);
		}
		return NULL;
	} else if (extype == EXPR_CONST) {
		if (*is_func_call) {
			errorfl(t, "Function call in constant expression");
		} else {
			errorfl(t, "Use of variable in constant expression");
		}
		return NULL;
	} else if (extype == EXPR_CONSTINIT
		|| extype == EXPR_OPTCONSTINIT
		|| extype == EXPR_OPTCONSTARRAYSIZE) {

		if (t->prev->type != TOK_OPERATOR
			|| *(int *)t->prev->data != TOK_OP_ADDR) {
#if 0
			if (*is_func_call) {
				/*
				 * This may be a call to a builtin function
				 * which yields a constant result, such as
				 * __builtin_offsetof(). In that case we
				 * want to evaluate the value as a constant
				 * expression
				 */
			}
#endif
			if (extype == EXPR_CONSTINIT) {
				if (*is_func_call) {
					errorfl(t,
					"Function call in constant expression");
				} else {
					errorfl(t, "Use of variable in constant "
					"expression");
				}
				return NULL;
			} else if (extype == EXPR_OPTCONSTINIT) {
				warningfl(t, "Variable array/struct "
				"initializers are not allowed in ISO "
				"C90 (only GNU C and C99)");
			} else {
				/* OPTCONSTARRAYSIZE */
				warningfl(t, "Variable-size arrays "
				"are not allowed in ISO C90 (only GNU "
				"C and C99)");
			}
		}
		if (dec->dtype->storage != TOK_KEY_STATIC
			&& dec->dtype->storage != TOK_KEY_EXTERN) {
			if (extype == EXPR_CONSTINIT) {
				errorfl(t,
			"Address of non-static variable taken in constant initializer");
				return NULL;
			} else {	
				warningfl(t, "Variable array/struct "
				"initializers are not allowed in ISO "
				"C90 (only GNU C and C99)");
			}
		}
	} else {
		/*
		 * in
		 * int foo; foo = 0; ... the second foo identifier can now
		 * be freed. This is not possible in case of a static variable,
		 * because it will have a different name (such as _Static_foo0)
		 */
		if (dec->dtype->storage != TOK_KEY_STATIC
			&& !is_implicit) {
			/* XXX this breaks with errorfl() for some reason!!
			 * see bs.c with valgrind
			 */
#if 0
			free(t->data);
			t->data = dec->dtype->name;
#endif
		}
	}

	context->var_lvalue = dec;
	context->is_lvalue = 1;
	copy_type(curtype, dec->dtype, 0);
		
	if (dec->vreg != NULL) {
		/* Variable is already register-backed */
		*err = 0;
		context->load = context->curitem = dec->vreg;
		return NULL;
	}

	if (eval) {
		*err = 0;
		vr = vreg_alloc(dec, NULL, NULL, NULL);
		context->load = dec->vreg = context->curitem = vr;
	} else {
		*err = 0; /* XXX ...?! */
		vr = vreg_alloc(NULL, NULL, NULL, dec->dtype);
		context->curitem = vr;
		return NULL;
	}	
	return ret;
}

static void
do_constant(
	struct context *context,
	struct token *t,
	int eval) {

	struct type		*curtype = &context->curtype;

	struct type		*ctype;
	struct vreg		*vr;

	ctype = make_basic_type(t->type);
	copy_type(curtype, ctype, 0);
	if (eval) {
		vr = vreg_alloc(NULL, t, NULL, NULL);
		context->load = context->curitem = vr;
	} else {
		vr = vreg_alloc(NULL, NULL, NULL, ctype);
		context->curitem = vr;
	}
}


static struct vreg *
do_string(struct context *context, struct token *t, int eval) {
	struct type	*curtype = &context->curtype;
	struct vreg	*vr;

	copy_type(curtype, ((struct ty_string *)t->data)->ty, 0);

	if (eval) {
		/*
		 * XXX 10/30/07: Hmmm, t will get the size of the
		 * string, not the pointer size, is this always
		 * desirable? It seems ``decay'' of string constants
		 * are not handled e.g. when loading a string
		 * address into a register to call a function.
		 * Check by aborting on vr->size != pr->size in
		 * vreg_map_preg() and call fputs("hello", stdout)
		 */
		vr = vreg_alloc(NULL, t, NULL, NULL);
		return context->load = context->curitem = vr;
	} else {
		vr = vreg_alloc(NULL, NULL, NULL, NULL);
		vr->size = backend->get_sizeof_type(curtype, NULL);
		vr->type = ((struct ty_string *)t->data)->ty;
		return context->curitem = vr;
	}	
}


/*
 * Increment/decrement current item. If we're dealing with postfix
 * increment/decrement operators, the result will be stored to the
 * item backing the virtual register we're operating on and then the
 * inc/dec step is reversed such that the old value of that vreg
 * will be used in the expression. This is sort of slow and kludgy 
 * but sort of works ok for now
 */
static void
do_inc_dec(struct context *context, int op, struct icode_list *il,
	struct token *optok,	
	int eval) {

	struct icode_instr	*ii;

	/* XXXXXXXXXXXXXXXXXXX use eval! */
	if (context->curtype.tlist != NULL) {
		/*int*/ long	factor =
			backend->get_sizeof_elem_type(
				&context->curtype);	
		struct token	*tmp;
		struct type	*tytmp;
		struct vreg	*tmpvr = vreg_alloc(NULL, NULL, NULL, NULL);

		if (context->curtype.code == TY_VOID) {
			/* if (! GNUC) {
			errorfl(optok, "Cannot "
				"perform pointer arithmetic "
				"on void pointers (cast to "
				"`(char *)' instead!)");
			} else { */
				warningfl(optok, "Pointer arithmetic "
					"on void pointers is a GNU C "
					"extension (you should cast "
					"to `(char *)' instead!)");

		}

		tmp = const_from_value(&factor, /*NULL*/make_basic_type(TY_LONG));
		tmpvr->from_const = tmp;
		tytmp = make_basic_type( /*TY_INT*/  TY_LONG);
		tmpvr->type = n_xmemdup(tytmp, sizeof *tytmp);
		tmpvr->size = backend->get_sizeof_type(tytmp, NULL);
		vreg_faultin(NULL, NULL, tmpvr, il, 0);
		vreg_faultin_protected(tmpvr, NULL, NULL,
			context->curitem, il, 0);

		/* XXX this stuff sucks... changing the tmpvr from int to long
		 * is better for 64bit systems..but the real solution is
		 * probbly to use ptrarit()!?
		 */
		if (op == TOK_OP_INCPRE
			|| op == TOK_OP_INCPOST) {
			ii = icode_make_add(context->curitem, tmpvr);
		} else {
			ii = icode_make_sub(context->curitem, tmpvr);
		}
		append_icode_list(il, ii);

		icode_make_store(curfunc, context->curitem,
			context->curitem, il);

		if (op == TOK_OP_INCPOST) {
			ii = icode_make_sub(context->curitem, tmpvr);
			append_icode_list(il, ii);
		} else if (op == TOK_OP_DECPOST) {
			ii = icode_make_add(context->curitem, tmpvr);
			append_icode_list(il, ii);
		}
	} else {
		if (context->curitem->type->code == TY_BOOL) {
			/* Ooops */
			struct reg	*r = NULL;
			struct reg	*oldvalue;

			/*
			if (lang == cplusplus) {
				if (IS_DECREMENT) {
					error("cannot decrement booolllllllllol");
				lol
				}
			}
			*/	

			vreg_faultin(NULL, NULL, context->curitem, il, 0);
			oldvalue = context->curitem->pregs[0];
			if (op == TOK_OP_INCPOST
				|| op == TOK_OP_DECPOST) {
				/* New value goes into r */
				r = ALLOC_GPR(curfunc, 1, il, NULL);
				icode_make_copyreg(r, oldvalue,
					context->curitem->type,
					context->curitem->type, il);
				/* XXX copyreg shouldn't set used */
				vreg_map_preg(context->curitem, oldvalue);
			}	

			if (op == TOK_OP_INCPRE
				|| op == TOK_OP_INCPOST) {
				/* ++ always sets to 1 */
				ii = icode_make_setreg(r? r: oldvalue, 1);
				append_icode_list(il, ii);
			} else {
				/* decrement - wraps around :( */
				struct icode_instr	*label;
				struct icode_instr	*endlabel;

				label = icode_make_label(NULL);
				endlabel = icode_make_label(NULL);
				ii = icode_make_cmp(context->curitem, NULL);
				append_icode_list(il, ii);
				ii = icode_make_branch(label, INSTR_BR_EQUAL,
					context->curitem);
				append_icode_list(il, ii);
				ii = icode_make_setreg(r? r: oldvalue, 0);
				append_icode_list(il, ii);
				ii = icode_make_jump(endlabel);
				append_icode_list(il, ii);
				append_icode_list(il, label);
				ii = icode_make_setreg(r? r: oldvalue, 1);
				append_icode_list(il, ii);
				append_icode_list(il, endlabel);

			}
			vreg_map_preg(context->curitem, r? r: oldvalue); 
			icode_make_store(curfunc, context->curitem,
				context->curitem, il);
			if (r != NULL) {
				free_preg(r, il, 1, 0);
				icode_make_copyreg(oldvalue, r,
					context->curitem->type,
					context->curitem->type, il);
				vreg_map_preg(context->curitem, r);
			}
		} else {
			/*
			 * 08/22/07: Allow floating point increment/decrement
			 */
			struct vreg	*fp_one = NULL;
			struct token	*otok;
			struct vreg	*orig_curitem = context->curitem;
			int		x87_trash = 0;

			if (is_floating_type(context->curitem->type)) {
				otok = fp_const_from_ascii("1.0",
					context->curitem->type->code);	
				fp_one = vreg_alloc(NULL, otok, NULL, NULL);

				if (!is_x87_trash(context->curitem)) {
					vreg_faultin(NULL, NULL, context->curitem,
						il, 0);
					reg_set_unallocatable(
						context->curitem->pregs[0]);
					vreg_faultin(NULL, NULL, fp_one, il, 0);
					reg_set_allocatable(
						context->curitem->pregs[0]);
				} else {
					x87_trash = 1;
				}	
			} else {
				vreg_faultin(NULL, NULL, context->curitem, il, 0);
			}	

			if (op == TOK_OP_INCPRE
				|| op == TOK_OP_INCPOST) {
				if (fp_one) {
					if (x87_trash) {
						context->curitem =
							x87_do_binop(fp_one,
					context->curitem, TOK_OP_PLUS, il);
					} else {
						ii = icode_make_add(context->curitem,
							fp_one);
					}
				} else {
					ii = icode_make_inc(context->curitem);
				}
			} else {
				if (fp_one) {
					if (x87_trash) {
						context->curitem =
							x87_do_binop(fp_one,
					context->curitem, TOK_OP_MINUS, il);
					} else {	
						ii = icode_make_sub(context->curitem,
							fp_one);
					}
				} else {	
					ii = icode_make_dec(context->curitem);
				}	
			}
			if (!x87_trash) {
				append_icode_list(il, ii);
			}
	
			if (x87_trash) {
				vreg_faultin_x87(NULL, NULL, context->curitem,
					il, 0);
				vreg_map_preg(orig_curitem,
					context->curitem->pregs[0]);
				icode_make_store(curfunc, orig_curitem,
					orig_curitem, il);
			} else {	
				icode_make_store(curfunc, context->curitem,
					context->curitem, il);
			}	
	
			if (op == TOK_OP_INCPOST) {
				if (fp_one) {
					if (x87_trash) {
						context->curitem =
							x87_do_binop(fp_one,
						context->curitem, TOK_OP_MINUS, il);
					} else {	
						ii = icode_make_sub(context->curitem,
							fp_one);
					}
				} else {
					ii = icode_make_dec(context->curitem);
				}
				if (!x87_trash) {
					append_icode_list(il, ii);
				}	
			} else if (op == TOK_OP_DECPOST) {
				if (fp_one) {
					if (x87_trash) {
						context->curitem =
							x87_do_binop(fp_one,
						context->curitem, TOK_OP_PLUS, il);
					} else {	
						ii = icode_make_add(context->curitem,
							fp_one);
					}
				} else {
					ii = icode_make_inc(context->curitem);
				}
				if (!x87_trash) {
					append_icode_list(il, ii);
				}	
			}
		}
	}	
	vreg_anonymify(&context->curitem, NULL, NULL, il);
	context->is_lvalue = 0;
}

static int 
unary_to_icode(struct context *context, struct token *op, int extype,
struct icode_list *il, int eval) {
	struct type		*curtype = &context->curtype;
	int			opval = *(int *)op->data;
	int			is_x87 = 0;
	struct icode_instr	*ii = NULL;
	struct vreg		*vr;
	struct reg		*r;
	struct operator		*operator;
	struct icode_instr	*label;

	operator = &operators[LOOKUP_OP2(opval)];

	if (is_floating_type(&context->curtype)) {
		if (opval != TOK_OP_LNEG
			&& opval != TOK_OP_UPLUS
			&& opval != TOK_OP_UMINUS
			&& opval != TOK_OP_ADDR
			&& opval != TOK_OP_INCPOST
			&& opval != TOK_OP_INCPRE
			&& opval != TOK_OP_DECPOST
			&& opval != TOK_OP_DECPRE) {
			errorfl(op, "`%s' operator applied to floating "
				"point type", op->ascii);
			return -1;
		}
		if (is_x87_trash(context->curitem)) {
			is_x87 = 1;
		}
	}

	if (IS_INCDEC_OP(opval)) {
		if (extype == EXPR_CONST || extype == EXPR_CONSTINIT) {
			errorfl(op, "Increment/decrement "
				"operators are not allowed in "
				"constant %ss",
			extype == EXPR_CONST? "expression"
				: "initializer");
			return -1;
		} else if (extype == EXPR_OPTCONSTINIT) {
			warningfl(op, "Variable array/struct "
				"initializers are not allowed in ISO "
				"C90 (only GNU C and C99)");
		} else if (extype == EXPR_OPTCONSTARRAYSIZE) {
			warningfl(op, "Variable-size arrays "
				"are not allowed in ISO C90 (only GNU "
				"C and C99)");
		}	
		if (is_basic_agg_type(curtype)) {
			errorfl(op, "Operator cannot be applied to basic "
				"aggregate types");
			return -1;
		}	
	}

	switch (opval) {
	case TOK_OP_DECPRE:
	case TOK_OP_INCPRE:
		if (!context->is_lvalue) {
			errorfl(op, "Pre-%s operator applied "
				"to something that is not an lvalue",
				opval == TOK_OP_INCPRE?
					"increment": "decrement");
			return -1;
		}
		if (eval) {
			do_inc_dec(context, opval, il, op, eval);
		}	
		context->is_lvalue = 0;
		break;
	case TOK_OP_INCPOST:
	case TOK_OP_DECPOST:
		if (!context->is_lvalue) {
			errorfl(op, "Post-%s operator applied "
				"to something that is not an lvalue",
				opval == TOK_OP_INCPOST?
					"increment": "decrement");
			return -1;
		}
		if (eval) {
			do_inc_dec(context, opval, il, op, eval);
		}	
		context->is_lvalue = 0;
		break;
	case TOK_OP_LNEG:
		/* 
		 * XXX as with the short circuit and relation operators,
		 * use conditional set instructions instead!
		 */
		if (eval) {
			r = ALLOC_GPR(curfunc, backend->get_sizeof_type(
				make_basic_type(TY_INT), NULL), il, NULL);
			reg_set_unallocatable(r);

			/* XXX hmm x87 stuff not encapsulated */
			vreg_faultin_x87(NULL, NULL, context->curitem, il, 0);
			vreg_anonymify(&context->curitem, NULL, NULL, il);
			reg_set_allocatable(r);

			ii = icode_make_setreg(r, 0);
			append_icode_list(il, ii);
#if 0
			ii = icode_make_cmp(context->curitem, NULL);
#endif
			ii = compare_vreg_with_zero(context->curitem, il);
			append_icode_list(il, ii);

			if (!context->curitem->is_multi_reg_obj) {
				free_pregs_vreg(context->curitem, il, 0, 0);
			}	

			/* If result zero, set to 1 */
			label = icode_make_label(NULL);
			ii = icode_make_branch(label, INSTR_BR_NEQUAL,
				context->curitem);
			append_icode_list(il, ii);

			if (context->curitem->is_multi_reg_obj) {
				ii = icode_make_cmp(context->curitem, NULL);
				append_icode_list(il, ii);
				ii = icode_make_branch(label, INSTR_BR_NEQUAL,
					context->curitem);
				append_icode_list(il, ii);
			}

			ii = icode_make_setreg(r, 1);
			append_icode_list(il, ii);
			append_icode_list(il, label);
		}

		context->is_lvalue = 0;
		context->curitem = vreg_alloc(NULL, NULL, NULL, NULL);

		if (eval) {
			vreg_map_preg(context->curitem, r);
		}	
		context->curitem->type = make_basic_type(TY_INT);
		copy_type(&context->curtype, context->curitem->type, 0);
		context->curitem->size =
			backend->get_sizeof_type(&context->curtype, NULL);
		ii = NULL;
		
		break;
	case TOK_OP_UPLUS:
	case TOK_OP_UMINUS:
		/*
		 * Operand must have arithmetic type (ie. integral or
		 * floating point) 
		 */
		if (!is_arithmetic_type(curtype)) {	
			errorfl(op, "Operand of `%s' operator must have "
				"arithmetic type", operator->name);
			return -1;
		}	
		if (opval == TOK_OP_UPLUS) {
			/* 
			 * This operator does not do anything except
			 * for causing integral promotion - it only
			 * exists for symmetry with unary ``-''
			 */
			struct type	*tytmp;
			struct type	*oldtype = context->curitem->type;

			if (is_x87 && eval) {
				context->curitem = x87_do_unop(context->curitem,
					TOK_OP_UPLUS, il);
				tytmp = oldtype;
			} else {
				tytmp = promote(&context->curitem, NULL, opval,
					op,
					eval? (void *)il: (void *)NULL, eval);
				if (tytmp == NULL) {
					return -1;
				}	
				vreg_anonymify(&context->curitem,NULL,NULL,il);
			}
			if (tytmp != oldtype) { 
				copy_type(&context->curtype, tytmp, 0); 
			}	
		} else {
			/* Minus */
			if (is_x87 && eval) {
				context->curitem = x87_do_unop(context->curitem,
					TOK_OP_UMINUS, il);
			} else {
				if (eval) {
					vreg_faultin(NULL, NULL, context->curitem,il,0);
				}	
				vreg_anonymify(&context->curitem,NULL,NULL,il);
				pro_mote(&context->curitem, il, eval);
				if (eval) {
					ii = icode_make_neg(context->curitem);
				}	
			}
		}	
		break;
	case TOK_OP_BNEG:
		if (!is_integral_type(curtype)) {	
			errorfl(op, "Operand of `%s' operator must have "
				"arithmetic type", operator->name);
			return -1;
		}
		if (eval) {
			vreg_faultin(NULL, NULL, context->curitem, il, 0);
		}	
		vreg_anonymify(&context->curitem, NULL, NULL, il);
		pro_mote(&context->curitem, il, eval);
		if (eval) {
			ii = icode_make_not(context->curitem);
			context->curitem = n_xmemdup(context->curitem,
				sizeof *context->curitem);
			vreg_map_preg(context->curitem, context->curitem->pregs[0]);
			if (context->curitem->is_multi_reg_obj) {
				vreg_map_preg2(context->curitem,
					context->curitem->pregs[1]);
			}	
		}
		context->is_lvalue = 0;
		break;
	case TOK_OP_ADDR:
		if (!context->is_lvalue) {
			errorfl(op, "Address-of operator applied "
				"to something that is not an lvalue");
			return -1;
		} else if (context->curtype.storage == TOK_KEY_REGISTER) {
			warningfl(op, "Address-of operator applied to "
				"`register' variable");	
		} else if (context->curtype.tlist != NULL
			&& context->curtype.tlist->type == TN_FUNCTION) {
			context->is_lvalue = 0;
			break;
		}	
		context->is_lvalue = 0;
		context->var_lvalue = NULL;
		if (eval) {
			struct vreg		*vr2 = NULL;
			
			if (context->curitem->parent) {
				vr2 = get_parent_struct(context->
						curitem);
				if (vr2->from_ptr) {
					vreg_faultin(NULL, NULL,
						vr2->from_ptr, il, 0);
					
				}
			}	
			
			ii = icode_make_addrof(context->curitem,
				il);
			append_icode_list(il, ii);
			if (vr2 && vr2->from_ptr) {
				free_preg(vr2->from_ptr->pregs[0],
					il, 0, 0);
			}	
		}	

		vr = vreg_alloc(NULL, NULL, NULL, NULL);
		if (eval) {
			vreg_map_preg(vr, ii->dat);
		}
		vr->type = addrofify_type(&context->curtype);
		vr->size = backend->
			get_sizeof_type(vr->type, NULL);
		context->curtype.tlist = /*tn*/vr->type->tlist;
		
		ii = NULL;
		context->curitem = vr;
		break;
	case TOK_OP_DEREF:
		if (curtype->tlist == NULL) {
			errorfl(op,
			"Attempt to dereference something that is not "
			"a pointer");
			return -1;
		}
		if (curtype->tlist->type == TN_FUNCTION
			|| (curtype->tlist->next
			&& curtype->tlist->next->type == TN_FUNCTION)) {
			/* (*f)(); is the same as f(); */
			break;
		}	
		if (curtype->code == TY_VOID
			&& curtype->tlist->next == NULL) {
			/* XXX This actually isn't true */
			errorfl(op,
				"Cannot dereference void pointers");
			return -1;
		}	
		if (curtype->incomplete
			|| (curtype->tstruc
			&& curtype->tstruc->incomplete
			&& curtype->tlist->next == NULL)) {
			errorfl(op,
			"Cannot dereference pointer to incomplete type");
			return -1;
		}

		/*
		 * XXX we need to pay more attention to storage class
		 * stuff when modifying types... This was just made
		 * aparent by an error message about taking the address
		 * of     
		 *          register char *p;   &p[i];
		 * ... here p[i] shouldn't be register-specified!
		 */
		context->curtype.storage = 0;
		copy_tlist(&context->curtype.tlist,
			context->curtype.tlist->next);	
		context->is_lvalue = 1;
		context->var_lvalue = NULL;	
		if (eval) {
			struct reg	*r;
			/* Fault in pointer */
			r = vreg_faultin(NULL, NULL, context->curitem, il, 0);
			ii = icode_make_indir(r);
			append_icode_list(il, ii);
			context->indir = 1;
			ii = NULL;
		}

		/*
		 * If the parent pointer also came from a pointer, it must
		 * be anonymified to avoid long chains of indirection
		 */
		if (context->curitem->from_ptr && eval) {
			vreg_anonymify(&context->curitem, NULL, NULL, il);
		}
		context->curitem
			= vreg_alloc(NULL, NULL, context->curitem, NULL);

		break;
	default:
		puts("WHAT??????????????");
		printf("%d\n", opval);
		abort();
	}
	
	if (ii != NULL && eval) {
		append_icode_list(il, ii);
	}

	return 0; 
}

static int 
do_cast(struct context *context, struct token *cast, 
struct icode_list *il, int eval) {
	struct decl		*d = cast->data;
	struct type		*ty = d->dtype;
	struct type		*curtype = &context->curtype;
	int			is_ptr_cast = 0;

	if (curtype->tlist != NULL) {
		/* Possibly dangerous cast */
	
		if (ty->tlist == NULL && ty->code != TY_VOID) {
			if (ty->code == TY_FLOAT
				|| ty->code == TY_DOUBLE
				|| ty->code == TY_LDOUBLE) {
				errorfl(cast, "Invalid cast of pointer "
						"to floating point type");
				return 1;
			} else if (ty->code != TY_ULONG
				&& ty->code != TY_ULLONG
				&& pedanticflag) {
				warningfl(cast, 
					"Dangerous cast of pointer to"
					" %s type, use "
					"`unsigned long' to improve "
					"portability",
					ty->code == TY_LONG? "signed long"
						: "small integral");
			}
		} else if (ty->tlist != NULL) {
			/* IMPORTANT: Depends on type order in token.h! */
			is_ptr_cast = 1;
			if (ty->code > curtype->code
				&& ty->code != TY_VOID
				&& curtype->code != TY_VOID
				&& (IS_CHAR(ty->code) /* XXX kludge ... */
					!= IS_CHAR(curtype->code)
				|| IS_SHORT(ty->code)
					!= IS_SHORT(curtype->code)
				|| IS_INT(ty->code)
					!= IS_INT(curtype->code)
				|| IS_LONG(ty->code)
					!= IS_LONG(curtype->code)
				|| IS_LLONG(ty->code)
					!= IS_LLONG(curtype->code)
				|| IS_FLOATING(ty->code))) {
				if (pedanticflag) {
					warningfl(cast,
					"Cast to pointer type with possibly "
					"higher alignment constraints");
				}	
			}	
		}
	}

	if (eval) {
		context->curitem =
			backend->icode_make_cast(context->curitem, ty, il);
	} else {
		context->curitem = n_xmemdup(context->curitem,
			sizeof *context->curitem);
		context->curitem->type = ty;
		context->curitem->size =
			backend->get_sizeof_type(ty, NULL);
	}	

	context->is_lvalue = 1;
	copy_type(curtype, ty, 0);
	return 0;
}

static void
do_comp_literal(struct context *context, struct token *item,
	struct icode_list *il, int eval) {

	struct comp_literal	*comp_lit = item->data;
	struct initializer	*init = comp_lit->init;
	struct type		*ty = comp_lit->struct_type;
	struct vreg		*vr;

	if (curscope == &global_scope) {
		/* ?@?@?@? WHAT ,this is never executed?!?!?! XXXXX */
		vr = vreg_alloc(NULL,NULL,NULL,NULL);
		vreg_set_new_type(vr, ty);
		unimpl();
	} else {
		if (eval) {
			vr = vreg_stack_alloc(ty, il, 1, init);
		} else {
			vr = vreg_alloc(NULL,NULL,NULL,NULL);
			vreg_set_new_type(vr, ty);
		}
	}

	copy_type(&context->curtype, ty, 0);
	
	/*
	 * Yes, you heard right, compound literals are lvalues! That is to
	 * make it possible to take their address (as a side effect, one 
	 * can also assign to them)
	 */
	context->is_lvalue = 1;
	context->curitem = vr;
	context->load = vr;
}

/*
 * Function that reads what I like to refer to as ``sub-expression''.
 * A sub-expression is just an argument to the binary and ternary
 * operators, and it includes all possible unary/prefix/postfix
 * operators (bitwise/logical negation, pre/post-inc/dec-rement,
 * function call, indirection, etc.)
 * This is typically an identifier or a constant, possibly including
 * operators, but it may also be a parenthesized expression, which
 * in turn consists of one or more sub-expressions.
 *
 * In other words, a ``sub-expression'' is what the standard refers to
 * as ``unary expression''
 *
 * Examples:
 * +foo + *bar--
 * ^^^^   ^^^^^^
 * *(x + 5) - foo? bar: baz
 *   ^   ^
 * ^^^^^^^^   ^^^  ^^^  ^^^
 */
struct s_expr *
get_sub_expr(struct token **tok, int delim, int delim2, int extype) {
	struct token		*t;
	struct token		*meat = NULL;
	struct token		*left;
	struct token		*right;
	struct token		*is_sizeof = NULL;
	struct token		*continue_at = NULL;
	struct token		*endp = NULL;
	struct expr		*ex = NULL;
	struct expr		*is_expr = NULL;
	struct decl		**decv;
	int			is_func_call = 0;
	struct token		*operators[128]; /* XXX */
	struct token		*operators_f[128];
	void			*res = NULL;
	struct icode_instr	*ii;
	struct icode_list	*il;
	struct s_expr		*ret = NULL;
	int			op;
	int			i;
	int			j = 0;

	t = *tok;
	il = alloc_icode_list();
	il->head = il->tail = NULL;

	/*
	 * Read prefix or unary operators, if any, and the
	 * identifier/constant/parenthesized expression
	 * constituting this sub-expression
	 */
	for (t = *tok, i = 0; t != NULL; t = t->next) {
		ii = NULL;
		if (expr_ends(t, delim, delim2)) {
			if (t == *tok) {
				errorfl(t, "Parse error at `%s'", t->ascii);
				return NULL;
			}	
			endp = t;
			goto out;
		} else if (t->type == TOK_PAREN_OPEN) {
			/* Must be cast or parenthesized expression */
			if (t->next == NULL) {
				errorfl(t, "Unexpected end of file");
				return NULL;
			}

			/*
			 * 10/01/07: This was missing the check for an
			 * identifier. Thus
			 *
			 *   typepdef int foo;
			 *   {
			 *       int foo;
			 *       (foo);
			 *
			 * ... the last line was interpreted as a cast
			 * expression. Not sure if this is fully correct
			 * now
			 */
			if (IS_TYPE(t->next)
				&& (t->next->type != TOK_IDENTIFIER
				|| lookup_symbol(curscope, t->next->data, 1)
				== NULL)) {
				/* Is cast */
				struct token	*casttok = t;
				struct type	*ty;

				operators[i++] = t;
				t = t->next;
				decv = parse_decl(&t, DECL_CAST);
				if (decv == NULL) {
					return NULL;
				}
				ty = decv[0]->dtype;
				casttok->data = decv[0];
				free(decv);
				if (t->next
					&& t->next->type == TOK_COMP_OPEN
					&& is_basic_agg_type(ty)) {
					struct initializer	*init;
					int			init_type;
					struct comp_literal	*comp_lit;

					if (curscope == &global_scope) {
						init_type = EXPR_CONSTINIT;
					} else {
						init_type = EXPR_OPTCONSTINIT;
					}

					/*
					 * Must be compound literal
					 */
					(void) next_token(&t);
					meat = casttok;
					init = get_init_expr(&t, init_type,
						ty, 1, 1);	
					if (init == NULL) {
						return NULL;
					}
					comp_lit = n_xmalloc(sizeof *comp_lit);
					comp_lit->init = init;
					comp_lit->struct_type = ty;
					meat->type = TOK_COMP_LITERAL;
					meat->data = comp_lit;
					operators[--i] = NULL;
					if (ty->tlist != NULL
						&& ty->tlist->type == TN_ARRAY_OF
						&& ty->tlist->arrarg->const_value
						== NULL) {
						/*
						 * No size specified - update
						 * using initializer info!
						 */
						init_to_array_size(ty, init);
					}
					break;
				} else {
					casttok->type = TOK_OP_CAST;
				}
			} else {
				/* Is parenthesized expression */
				t = t->next;
				ex = parse_expr(&t, TOK_PAREN_CLOSE, 0, extype, 0);
				if (ex == NULL) {
					return NULL;
				}

				is_expr = ex;
				continue_at = t;
				break;
			}
		} else if (t->type == TOK_OPERATOR) {
			/* Must be unary prefix operator */
			if ((op = ambig_to_unary(t, 1)) == -1) { 
				return NULL;
			}
			*(int *)t->data = op;
			operators[i++] = t;
		} else if (t->type == TOK_KEY_SIZEOF
			|| t->type == TOK_KEY_ALIGNOF) {
			if (do_sizeof(t, &endp, &is_sizeof,
				extype, delim, delim2) == -1) {
				return NULL;
			}
			operators[i++] = t;
			break;
		} else if (t->type == TOK_IDENTIFIER) {
			if (t->next && t->next->type == TOK_PAREN_OPEN) {
				/* Is function call */
				is_func_call = 1; 
			}
			meat = t;
			break;
		} else if (t->type == TOK_STRING_LITERAL
				|| IS_CONSTANT(t->type)) {
			meat = t;
			break;
		} else {
			errorfl(t, "Parse error at `%s'(#1)", t->ascii);
			abort();
		}
	}

	if (is_expr) {
		right = continue_at;
		if (next_token(&right) != 0) {
			return NULL;
		}
	} else if (is_func_call) {
		right = meat->next;
	} else if (!is_sizeof) {
		if (meat != *tok) {
			left = meat->prev;
		} else {
			left = NULL;
		}

		if (meat->type == TOK_COMP_LITERAL) {
			right = t;
		} else {	
			right = meat;

			if (next_token(&right) != 0) {
				return NULL;
			}
		}	
	} else {
		right = NULL;
	}

	/*
	 * Compare prefix with postfix operators and
	 * bind them accordingly
	 */
	if (i > 0) {
		left = operators[--i];
	} else {
		left = NULL;
	}

	j = 0;
	for (;;) {
		if (right != NULL) {
			endp = right;
			if (expr_ends(right, delim, delim2)) {
				right = NULL;
			}
		}

		/*
		 * Postfix operators always have higher precedence
		 * than prefix and unary operators
		 */
		res = NULL;
		if (right != NULL) {
			if (right->type == TOK_PAREN_OPEN) {
				struct token		*old_right = right;
				struct fcall_data	*fres;

				/*
				 * Must be function call. This isn't allowed
				 * in constant expressions if we have a real
				 * function, but there are some builtins,
				 * such as __builtin_offsetof(), which yield
				 * a constant value and should be computable
				 * in constant expressions. Thus we check if
				 * that's the case here
				 */
				res = do_func_call(meat, &right, 1); 
				if (res == NULL) {
					return NULL;
				}
				fres = res;

				if (fres->builtin != NULL
					&& fres->builtin->type
					== BUILTIN_OFFSETOF) {
					goto const_ok;
				}
				if (extype == EXPR_CONST
					|| extype == EXPR_CONSTINIT) {
					errorfl(right,
						"Function calls are not "
						"allowed in constant %ss",
						extype == EXPR_CONST?
							"expression":
							"initializer");
					return NULL;
				} else if (extype == EXPR_OPTCONSTINIT) {
					warningfl(right, "Variable array/struct "
					"initializers are not allowed in ISO "
					"C90 (only GNU C and C99)");
				} else if (extype == EXPR_OPTCONSTARRAYSIZE) {
					warningfl(right, "Variable-size arrays "
					"are not allowed in ISO C90 (only GNU "
					"C and C99)");
				}
const_ok:		
				if (fres->builtin
					&& fres->builtin->type == BUILTIN_EXPECT) {
					if (is_expr) {
						errorfl(right, "Parse error at `%s'",
							right->ascii);
						return NULL;
					}
					is_expr = fres->builtin->args[0];
				} else {
					operators_f[j] = old_right;
					operators_f[j++]->data = res;
				}
			} else if (right->type == TOK_OPERATOR) {
				/* 
				 * Must be postfix operator or end of
				 * sub-expression
				 */
				op = ambig_to_unary(right, 0);
				if (IS_UNARY(op)) {
					/* XXX handle op */
					operators_f[j++] = right;
					*(int *)right->data = op;
				} else if (op == TOK_OP_STRUMEMB
						|| op == TOK_OP_STRUPMEMB) {
					operators_f[j] = right;
					right->type = op;
					if (next_token(&right) != 0) {
						return NULL;
					}
					if (right->type != TOK_IDENTIFIER) {
						errorfl(right,
							"Structure indirection"
							" operator not "
							"followed by structure"
							" member name");
						return NULL;
					}
					operators_f[j++]->data = right;
				} else {
					right = NULL;
				}
			} else if (right->type == TOK_ARRAY_OPEN) {
				operators_f[j] = right;
				if (next_token(&right) != 0) {
					return NULL;
				}
				res = parse_expr(&right, TOK_ARRAY_CLOSE, 0, 0, 1);
				if (res == NULL) {
					return NULL;
				}
				operators_f[j++]->data = res;
			} else {
				right = NULL;
			}
			if (right) right = right->next;
		} else if (left != NULL) {
			operators_f[j++] = left;
			if (i > 0) {
				left = operators[--i];
			} else {
				left = NULL;
			}
		} else {
			break;
		}
	}
	operators_f[j++] = NULL;

out:
	*tok = endp;
	if (meat == NULL && is_expr == NULL && is_sizeof == NULL) {
		errorfl(endp, "Parse error at `%s'", endp->ascii);
	}	

	ret = alloc_s_expr();
	memcpy(ret->operators, operators_f, j * sizeof *operators_f);
	ret->is_expr = is_expr;
	ret->meat = meat;
	ret->is_sizeof = is_sizeof;

	return ret;
}


struct vreg *
s_expr_to_icode(
	struct s_expr *s,
	struct vreg *lvalue,
	struct icode_list *il,
	int eval) {

	struct context		context;
	struct icode_instr	*ii;
	struct fcall_data	*fcall;
	struct type_node	*funcnode = NULL;
	struct decl		*is_func_call = NULL;
	struct type		*fty;
	int			i;

	context.curtype.code = 0;
	context.var_lvalue = NULL;
	context.is_lvalue = 0;
	context.curitem = NULL;
	context.indir = 0;
	context.load = NULL;

	s->only_load = 1;
	if (s->is_expr) {
		context.curitem = expr_to_icode(s->is_expr, lvalue,
			il, 0, eval);
		if (context.curitem == NULL) {
			return NULL;
		}	
		copy_type(&context.curtype, context.curitem->type, 0);

		/*
		 * The property of being an lvalue is kept across parenthses;
		 * int x; &(x);
		 * and
		 * int *p; ... ++(*p);
		 * both have to work
		 */
		if (s->is_expr->op == 0) {
			/* data may be NULL if this is a statement-as-expr */
			if (s->is_expr->data != NULL) {
				context.is_lvalue =
					s->is_expr->data->is_lvalue;
				context.var_lvalue =
					s->is_expr->data->var_lvalue;
			}
		}	
	} else if (s->meat) {
		if (s->meat->type == TOK_IDENTIFIER) {
			struct decl	**is_func_call_p = NULL;	
			int		err;

			if (s->operators[0] != NULL
				&& s->operators[0]->type == TOK_PAREN_OPEN) {
				is_func_call_p = &is_func_call;
			}
			ii = do_identifier(&context, s->meat,
				s->extype, is_func_call_p, &err, eval);
			if (ii == NULL) {
				if (err) {
					return NULL;
				}
			} else {
				append_icode_list(il, ii);
			}
		} else if (s->meat->type == TOK_STRING_LITERAL) {
			(void) do_string(&context, s->meat, eval);
		} else if (IS_CONSTANT(s->meat->type)) {
			do_constant(&context, s->meat, eval);
		} else if (s->meat->type == TOK_COMP_LITERAL) {
			do_comp_literal(&context, s->meat, il, eval);
		} else {
			puts("BUG: bad item in sub-expression");
			exit(EXIT_FAILURE);
		}
	} else if (s->is_sizeof) {	
		;
	} else {
		puts("BUG: sub-expression has no body??");
			exit(EXIT_FAILURE);
	}
	s->load = context.load;
	if (s->extype == EXPR_CONST
		|| s->extype == EXPR_CONSTINIT
		|| s->extype == EXPR_OPTCONSTINIT
		|| s->extype == EXPR_OPTCONSTARRAYSIZE) {
		return s->res = context.curitem;
	}

	for (i = 0; s->operators[i] != NULL; ++i) {
		s->only_load = 0;
		
		switch (s->operators[i]->type) {
		case TOK_OP_STRUPMEMB:
		case TOK_OP_STRUMEMB:

			if (do_struct_member(
				s->operators[i],
				&context,
				il,
				eval) != 0) {
				return NULL;
			}
			context.curitem->struct_ret = 0;
			break;
		case TOK_OPERATOR:
			if (unary_to_icode(&context,
				s->operators[i], s->extype, il, eval) != 0) {
				return NULL;
			}
			break;
		case TOK_KEY_SIZEOF:
		case TOK_KEY_ALIGNOF:	
		case TOK_SIZEOF_VLA_TYPE:	
		case TOK_SIZEOF_VLA_EXPR:	
			if (s->operators[i]->type == TOK_SIZEOF_VLA_TYPE) {
				struct type	*ty;

				unimpl();
				ty = s->operators[i]->data;
				vla_decl_to_icode(ty, il);
			} else if (s->operators[i]->type == TOK_SIZEOF_VLA_EXPR) {
				struct s_expr	*s_ex;
				struct vreg	*vr;

				s_ex = s->operators[i]->data;
				vr = s_expr_to_icode(s_ex, NULL, il, 1);
				if (vr == NULL) {
					return NULL;
				}
				vr = backend->get_sizeof_vla_type(vr->type, il);
				context.curitem = vr;
			} else {
				context.curitem = s->operators[i]->data;		
			}
			context.curitem->type = backend->get_size_t();
			copy_type(&context.curtype, context.curitem->type, 0);
			context.curitem->size = backend->
				get_sizeof_type(context.curitem->type, NULL);
			break;
		case TOK_PAREN_OPEN:
			/* Function call */
			fcall = s->operators[i]->data;
			fcall->lvalue = lvalue;
			if ((fcall->callto = context.var_lvalue) == NULL
				&& fcall->builtin == NULL) {
				fcall->calltovr = context.curitem;
				if (fcall->calltovr == NULL) {
					/* XXX error message?! */
					return NULL;
				}
			} else if (fcall->builtin == NULL) {
				fcall->calltovr = context.curitem;
			}

			fty = NULL;
			if (fcall->callto != NULL) {
				fty = fcall->callto->dtype;
			} else if (fcall->calltovr != NULL) {
				fty = fcall->calltovr->type;
			}
			if (fty && fty->tlist == NULL) {
				errorfl(s->operators[i],
					"Call to something that is not "
					"a function");
				return NULL;
			}	
					
			if (fty != NULL) { 
				fcall->functype = NULL;
				if (fty->tlist->type == TN_FUNCTION) {
					fcall->functype = fty->tlist->tfunc;
					funcnode = fty->tlist;
				} else if (fty->tlist->next != NULL
					&& fty->tlist->next->type
					== TN_FUNCTION) {
					fcall->functype =
						fty->tlist->next->tfunc;
					funcnode = fty->tlist->next;
				}
				if (fcall->functype == NULL) {
					errorfl(s->operators[i],
						"Call to something that is not "
						"a function");
					return NULL;
				}
			}

			if (fcall->builtin != NULL) {
				context.curitem =
					fcall->builtin->
						builtin->toicode(fcall, il);
			} else {
				/*
				 * 08/16/07: Check whether we have to create
				 * an anonymous stack struct to hold the
				 * result of the function call. This is
				 * necessary if the result is not directly
				 * assigned to a variable, but e.g. passed
				 * to another function, or some operator is
				 * applied to it;
				 *
				 * struct foo func();
				 * ...
				 * int x = func().x;
				 * ...
				 * void func2(struct foo f);
				 * ...
				 * func2(func());
				 */
				int	needanon = 0;

				if (s->operators[i + 1] != NULL
					&& (fty->code == TY_STRUCT
					|| fty->code == TY_UNION)
					&& funcnode->next == NULL) {
					/* Need anonymous struct */
					needanon = 1;
				}
				fcall->need_anon = needanon;
				context.curitem = fcall_to_icode(fcall, il,
						s->meat, eval);
			}
			if (context.curitem == NULL) {
				return NULL;
			}	
			copy_type(&context.curtype, context.curitem->type, 0);
			break;
		case TOK_OP_CAST:
			if (do_cast(&context, s->operators[i], il, eval)) {
				return NULL;
			}
			break;
		case TOK_ARRAY_OPEN:
			if (do_subscript(&context, s->operators[i],
					il, eval) != 0) {
				return NULL; 
			}	
			break;
		}
	}
	s->var_lvalue = context.var_lvalue;
	s->is_lvalue = context.is_lvalue;
	s->type = n_xmalloc(sizeof(struct type));
	copy_type(s->type, &context.curtype, 1);

	return s->res = context.curitem;
}



syntax highlighted by Code2HTML, v. 0.9.1