/* Expression parsing for Ksi.
   Copyright (C) 2001, 2002 Michal Moskal (malekith@pld-linux.org)

This file is part of GCC.

GCC is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.

GCC is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with GCC; see the file COPYING.  If not, write to
the Free Software Foundation, 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA.  */


#include "ksi.h"

int lvalue_p(tree ref);

static tree typecheck_exp(keyword_t op, tree t1, tree t2)
{
	int err_kind = 0;
	int err_same = 0;
	int err_size = 0;
	int err_sign = 0;
	tree ret = t1;

	if (TYPE_ABSTRACT_P(t1) || TYPE_ABSTRACT_P(t2))
		fatal_error("(%s ...) does not allow abstract operands",
			    keyword_name(op));
		
	switch (op) {
	case kw_minus:
		if (TREE_CODE(t1) == INTEGER_TYPE ||
		    TREE_CODE(t1) == REAL_TYPE) {
			if (TREE_CODE(t1) != TREE_CODE(t2))
				err_same++;
		    	if (TREE_CODE(t1) == INTEGER_TYPE &&
			    !TREE_UNSIGNED(t1) != !TREE_UNSIGNED(t2))
			    	err_sign++;
			if (TYPE_PRECISION(t1) != TYPE_PRECISION(t2))
				err_size++;
		} else if (TREE_CODE(t1) == POINTER_TYPE && 
			   TREE_CODE(t2) == POINTER_TYPE) {
			same_types(op, t1, t2);
			ret = type_for_size(TYPE_PRECISION(t1), 0);
		} else
			err_kind++;
		break;
		
	case kw_plus:
		if ((TREE_CODE(t1) == POINTER_TYPE && 
		     TREE_CODE(t2) == INTEGER_TYPE) ||
		    (TREE_CODE(t2) == POINTER_TYPE && 
		     TREE_CODE(t1) == INTEGER_TYPE)) {
		     	/* until pre28 there was no size check
			 * here.... */
			if (TYPE_PRECISION(t1) != TYPE_PRECISION(t2))
				err_size++;
			ret = (TREE_CODE(t1) == POINTER_TYPE) ? t1 : t2;
		} else if (TREE_CODE(t1) == INTEGER_TYPE ||
		           TREE_CODE(t1) == REAL_TYPE) {
			if (TREE_CODE(t1) != TREE_CODE(t2))
				err_same++;
		    	if (TREE_CODE(t1) == INTEGER_TYPE &&
			    !TREE_UNSIGNED(t1) != !TREE_UNSIGNED(t2))
			    	err_sign++;
			if (TYPE_PRECISION(t1) != TYPE_PRECISION(t2))
				err_size++;
		} else
			err_kind++;
		break;
	
	case kw_mult:
		/* same int,uint,real */
		if (TREE_CODE(t1) != INTEGER_TYPE &&
		    TREE_CODE(t1) != REAL_TYPE)
		    	err_kind++;
	    	if (TREE_CODE(t1) == INTEGER_TYPE &&
		    !TREE_UNSIGNED(t1) != !TREE_UNSIGNED(t2))
		    	err_sign++;
		if (TREE_CODE(t1) != TREE_CODE(t2))
			err_same++;
		if (TYPE_PRECISION(t1) != TYPE_PRECISION(t2))
			err_size++;
		break;

	case kw_trunc_div:
	case kw_ceil_div:
	case kw_floor_div:
	case kw_round_div:
	case kw_exact_div:
	case kw_trunc_mod:
	case kw_ceil_mod:
	case kw_floor_mod:
	case kw_round_mod:
	case kw_bit_ior:
	case kw_bit_xor:
	case kw_bit_and:
	case kw_bit_andtc:
	case kw_lshift:
	case kw_rshift:
	case kw_lrotate:
	case kw_rrotate:
		/* same int,uint */
		if (TREE_CODE(t1) != INTEGER_TYPE)
		    	err_kind++;
		if (TREE_CODE(t1) != TREE_CODE(t2))
			err_same++;
	    	if (!TREE_UNSIGNED(t1) != !TREE_UNSIGNED(t2))
		    	err_sign++;
		if (TYPE_PRECISION(t1) != TYPE_PRECISION(t2))
			err_size++;
		break;
	
	case kw_unlt:
	case kw_unle:
	case kw_ungt:
	case kw_unge:
	case kw_uneq:
	case kw_ordered:
	case kw_unordered:
		ret = bool_type_node;
	case kw_rdiv:
		/* same real */
		if (TREE_CODE(t1) != REAL_TYPE)
		    	err_kind++;
		if (TREE_CODE(t1) != TREE_CODE(t2))
			err_same++;
		if (TYPE_PRECISION(t1) != TYPE_PRECISION(t2))
			err_size++;
		break;
	
	
	case kw_truth_andif:
	case kw_truth_orif:
	case kw_truth_and:
	case kw_truth_or:
	case kw_truth_xor:
		/* bool */
		if (TREE_CODE(t1) != BOOLEAN_TYPE || 
		    TREE_CODE(t2) != BOOLEAN_TYPE)
		    	err_kind++;
		break;
	
	case kw_lt:
	case kw_le:
	case kw_gt:
	case kw_ge:
		ret = bool_type_node;
	case kw_min:
	case kw_max:
		/* same real,int,uint,ptr */
		if (TREE_CODE(t1) != INTEGER_TYPE &&
		    TREE_CODE(t1) != REAL_TYPE &&
		    TREE_CODE(t1) != POINTER_TYPE)
		    	err_kind++;
		if (TREE_CODE(t1) != TREE_CODE(t2))
			err_same++;
		if (err_kind == 0 && err_same == 0 &&
		    TREE_CODE(t1) == POINTER_TYPE)
			same_types(op, t1, t2);
	    	if (TREE_CODE(t1) == INTEGER_TYPE &&
		    !TREE_UNSIGNED(t1) != !TREE_UNSIGNED(t2))
		    	err_sign++;
		if (TYPE_PRECISION(t1) != TYPE_PRECISION(t2))
			err_size++;
		break;
	
	case kw_eq:
	case kw_ne:
		/* same real,int,uint,ptr,bool */
		if (TREE_CODE(t1) != INTEGER_TYPE &&
		    TREE_CODE(t1) != REAL_TYPE &&
		    TREE_CODE(t1) != BOOLEAN_TYPE &&
		    TREE_CODE(t1) != POINTER_TYPE)
		    	err_kind++;
		if (TREE_CODE(t1) != TREE_CODE(t2))
			err_same++;
		if (err_kind == 0 && err_same == 0 &&
		    TREE_CODE(t1) == POINTER_TYPE)
			same_types(op, t1, t2);
	    	if (TREE_CODE(t1) == INTEGER_TYPE &&
		    !TREE_UNSIGNED(t1) != !TREE_UNSIGNED(t2))
		    	err_sign++;
		if (TYPE_PRECISION(t1) != TYPE_PRECISION(t2))
			err_size++;
		ret = bool_type_node;
		break;

	case kw_postinc:
	case kw_preinc:
	case kw_postdec:
	case kw_predec:
		if ((TREE_CODE(t1) == POINTER_TYPE && 
		     TREE_CODE(t2) == INTEGER_TYPE)) {
			if (TYPE_PRECISION(t1) != TYPE_PRECISION(t2))
				err_size++;
		} else if (TREE_CODE(t1) == INTEGER_TYPE ||
			   TREE_CODE(t1) == REAL_TYPE) {
			if (TREE_CODE(t1) != TREE_CODE(t2))
				err_same++;
			if (TREE_CODE(t1) == INTEGER_TYPE &&
			    !TREE_UNSIGNED(t1) != !TREE_UNSIGNED(t2))
				err_sign++;
			if (TYPE_PRECISION(t1) != TYPE_PRECISION(t2))
				err_size++;
		} else
			err_kind++;
		break;
		
	default:
		ice();
	}

	if (err_kind)
		fatal_error("(%s ...) does not allow operands of this type",
			    keyword_name(op));
	if (err_same)
		fatal_error("(%s ...) operand's types does not agree",
			    keyword_name(op));
	if (err_size)
		fatal_error("(%s ...) operand's types does not agree in size",
			    keyword_name(op));
	
	if (err_sign)
		fatal_error("(%s ...) operand's types differ in signess",
			    keyword_name(op));
	
	return ret;
}

/* (OP_NAME, EXP1, EXP2) */
tree compile_bin(keyword_t op, enum tree_code code, tree *type)
{
	struct term_state ctx;
	tree t1, t2, e1, e2;

	init_term(&ctx, op);
	e1 = compile(get_keyword(&ctx), &t1);
	e2 = compile(get_keyword(&ctx), &t2);
	fini_term(&ctx);

	*type = typecheck_exp(op, t1, t2);
	
	return build(code, *type, e1, e2);
}

tree compile_prepost(keyword_t op, enum tree_code code, tree *type)
{
	struct term_state ctx;
	tree t1, t2, e1, e2;

	init_term(&ctx, op);
	e1 = compile(get_keyword(&ctx), &t1);
	e2 = compile(get_keyword(&ctx), &t2);
	fini_term(&ctx);

	*type = typecheck_exp(op, t1, t2);
	
	if (!lvalue_p(e1))
		fatal_error("first operand of (%s ...) has to be "
			    "proper lvalue", keyword_name(op));
		
	return build(code, *type, e1, e2);
}

tree make_string(int len, const char *data)
{
	tree value;

	value = build_string(len, data);
	TREE_TYPE(value) = 
		build_array_type(char_type_node,
			    	 build_index_type(build_int_2(len - 1, 0)));

	TREE_CONSTANT(value) = 1;
	TREE_READONLY(value) = 0;
	TREE_STATIC(value) = 1;

	return build1(ADDR_EXPR, ptr_type_node, value);
}
		
tree compile_size_of(tree *type)
{
	struct term_state ctx;
  	enum tree_code code;
	tree size;
	tree t;
	
	init_term(&ctx, kw_size_of);
	t = parse_type(get_keyword(&ctx));
	fini_term(&ctx);
	
	code = TREE_CODE (t);

	switch (code) {
	case FUNCTION_TYPE:
		fatal_error("sizeof applied to a function type");
		break;
	case VOID_TYPE:
		fatal_error("sizeof applied to a void type");
		break;
	default:
		break;
	}
	
	if (!COMPLETE_TYPE_P(t))
		fatal_error("sizeof applied to an incomplete type");
		
	size = TYPE_SIZE_UNIT(t);
	*type = sizetype;

	return fold(build1(NOP_EXPR, sizetype, size));
}

/* const(type(uint, 32), 2818) */
/* const(type(ptr), "Hello world") */
/* const(type(bool), 1|0) */
tree compile_const(tree *type)
{
	struct term_state ctx;
	struct arg a;
	tree e;

	init_term(&ctx, kw_const);
	
	*type = parse_type(get_keyword(&ctx));

	switch (TREE_CODE(*type)) {
	case INTEGER_TYPE:
		get_term_arg(&ctx, arg_int|arg_uint, &a);
		e = build_int_2(a.u.val_int.low, a.u.val_int.high);
		TREE_TYPE(e) = *type;
		break;
	case REAL_TYPE:
		get_term_arg(&ctx, arg_float, &a);
		e = build_real(*type, REAL_VALUE_ATOF(a.u.val_float, 
						      TYPE_MODE(*type)));
		break;
	case BOOLEAN_TYPE:
		get_term_arg(&ctx, arg_int|arg_uint, &a);
		if (a.u.val_int.high != 0 ||
		    (a.u.val_int.low != 0 && a.u.val_int.low != 1))
		    	fatal_error("bad boolean constant, non 0 or 1");
		e = build_int_2(a.u.val_int.low, a.u.val_int.high);
		TREE_TYPE(e) = *type;
		break;
	case POINTER_TYPE:
		get_term_arg(&ctx, arg_int|arg_uint|arg_string, &a);
		if (a.sel == arg_string)
			e = make_string(a.u.val_string.len,
				        a.u.val_string.data);
		else
			e = build1(CONVERT_EXPR, *type,
				   build_int_2(a.u.val_uint.low, 
				   	       a.u.val_uint.high));
		break;
	default:
		ice();
	}

	fini_term(&ctx);

	return e;
}

tree compile_enumref(tree *type)
{
	struct term_state ctx;
	tree id, val, e;

	init_term(&ctx, kw_enumref);
	id = get_id(&ctx);
	fini_term(&ctx);

	if ((val = IDENTIFIER_ENUM_VALUE(id)) == NULL_TREE)
		fatal_error("enumerator `%s' not defined",
		            IDENTIFIER_POINTER(id));
	
	*type = type_for_size(TYPE_PRECISION(TREE_TYPE(val)), 0);
	
	e = copy_node(val);
	TREE_TYPE(e) = *type;

	return e;
}

/* compound(EXP, EXP) */
tree compile_compound(tree *type)
{
	tree e1, e2;
	struct list_state ctx;
	struct term_state tctx;

	init_term(&tctx, kw_compound);

	expect_list(&ctx);
	
	e1 = NULL;
	
	for (;;) {
		keyword_t kw = get_keyword_from_list(&ctx);

		if (kw == kw__error)
			break;
		
		e2 = compile(kw, type);
		if (e1)
			e1 = build(COMPOUND_EXPR, *type, e1, e2);
		else
			e1 = e2;
	}

	if (e1 == NULL)
		fatal_error("empty (compound [])");
		
	fini_term(&tctx);
	
	return e1;
}

/* cond(COND, TRUE_EXP, FALSE_EXP) */
tree compile_cond(tree *type)
{
	struct term_state ctx;
	tree cond, e1, e2, t, t1, t2, exp;

	init_term(&ctx, kw_cond);
	cond = compile(get_keyword(&ctx), &t);
	if (TREE_CODE(t) != BOOLEAN_TYPE)
		fatal_error("condition for (cond ...) has non-bool type");
	e1 = compile(get_keyword(&ctx), &t1);
	e2 = compile(get_keyword(&ctx), &t2);
	fini_term(&ctx);

	same_types(kw_cond, t1, t2);
	
	*type = t1;
	exp = build(COND_EXPR, *type, cond, e1, e2);

	return exp;
}

/* indirect(EXP) */
tree compile_indirect(tree *type)
{
	struct term_state ctx;
	tree exp;

	init_term(&ctx, kw_indirect);
	exp = compile(get_keyword(&ctx), type);
	fini_term(&ctx);

	if (!POINTER_TYPE_P(*type))
		fatal_error("trying to dereference non-pointer");
	
	/* derefernce: */
	*type = TREE_TYPE(*type);
	
	if (TREE_CODE(*type) == VOID_TYPE)
		fatal_error("trying to dereference generic pointer");
	
	exp = build1(INDIRECT_REF, *type, exp);

	return exp;
}

/* modify(EXP_TARGET, EXP_SOURCE) */
tree compile_modify(tree *type)
{
	struct term_state ctx;
	tree e1, e2, t1, t2, exp;

	init_term(&ctx, kw_modify);
	e1 = compile(get_keyword(&ctx), &t1);
	e2 = compile(get_keyword(&ctx), &t2);
	fini_term(&ctx);

	same_types(kw_modify, t1, t2);
	if (TREE_CODE(t1) == VOID_TYPE)
		fatal_error("assigment to variable of type void");

	if (!lvalue_p(e1))
		fatal_error("left-side operand of (modify ...) "
			    "is not valid lvalue");
	
	*type = t1;

	exp = build(MODIFY_EXPR, *type, e1, e2);
	
	return exp;
}

/* nop() */
tree compile_nop(tree *type)
{
	arg0_term(kw_nop);
	*type = void_type_node;
	return build1(NOP_EXPR, *type, integer_zero_node);
}

/* convert(TYPE, EXP) */
tree compile_convert(tree *type)
{
	struct term_state ctx;
	tree from_type, exp;

	init_term(&ctx, kw_convert);
	*type = parse_type(get_keyword(&ctx));
	exp = compile(get_keyword(&ctx), &from_type);
	fini_term(&ctx);

	/*
	 * Make (convert ...) work on real->integer
	 * and the other way arround.
	 */
	if (TREE_CODE(*type) == INTEGER_TYPE &&
	    TREE_CODE(TREE_TYPE(exp)) == REAL_TYPE)
	    	exp = build1(FIX_TRUNC_EXPR, *type, exp);
	else if (TREE_CODE(*type) == REAL_TYPE &&
		 TREE_CODE(TREE_TYPE(exp)) == INTEGER_TYPE)
	    	exp = build1(FLOAT_EXPR, *type, exp);
	else
		exp = build1(CONVERT_EXPR, *type, exp);

	return exp;
}

/* fix(ID, TYPE, EXP) */
tree compile_fix(tree *type, enum tree_code fixmode)
{
	struct term_state ctx;
	tree t, exp;

	init_term(&ctx, kw_fix_trunc);
	*type = parse_type(get_keyword(&ctx));
	if (TREE_CODE(*type) != INTEGER_TYPE)
		fatal_error("type for (fix* ...) isn't integer");
	exp = compile(get_keyword(&ctx), &t);

	fini_term(&ctx);

	exp = build1(fixmode, *type, exp);

	return exp;
}

/* float(TYPE, EXP) */
tree compile_float(tree *type)
{
	struct term_state ctx;
	tree t, exp;

	init_term(&ctx, kw_float);
	
	*type = parse_type(get_keyword(&ctx));
	if (TREE_CODE(*type) != REAL_TYPE)
		fatal_error("type for (float_expr ...) isn't real");
	exp = compile(get_keyword(&ctx), &t);

	fini_term(&ctx);

	exp = build1(FLOAT_EXPR, *type, exp);

	return exp;
}

static tree typecheck_unary(keyword_t op, tree type)
{
	tree ret = type;
	
	switch (op) {
	case kw_negate:
	case kw_abs:
		if (TYPE_ABSTRACT_P(type) ||
		    (TREE_CODE(type) != INTEGER_TYPE &&
		     TREE_CODE(type) != REAL_TYPE) ||
		    (TREE_CODE(type) == INTEGER_TYPE &&
		     TREE_UNSIGNED(type)))
			fatal_error("negate and abs needs integer "
				    "or real argument");
		break;

	case kw_ffs:
	case kw_bit_not:
		if (TYPE_ABSTRACT_P(type) || 
		    TREE_CODE(type) != INTEGER_TYPE)
			fatal_error("ffs and bit_not needs integer "
				    "argument");
		break;
		
	case kw_truth_not:
		if (TYPE_ABSTRACT_P(type) || 
		    TREE_CODE(type) != BOOLEAN_TYPE)
			fatal_error("truth_not needs boolean "
				    "argument");
		break;

	case kw_non_lvalue:
		break;
		
	case kw_addr:
		ret = build_pointer_type(type);
		break;
	
	default:
		ice();
	}

	return ret;
}

static void check_addr(tree exp)
{
	switch (TREE_CODE(exp)) {
	case PARM_DECL:
	case VAR_DECL:
		TREE_ADDRESSABLE(exp) = 1;
		break;
	case COMPONENT_REF:
		check_addr(TREE_OPERAND(exp, 0));
		break;
	case INDIRECT_REF:
		/* it's always OK to get address of sth indirected,
		 * since for all X ADDRS(INDIRECT(X)) == X */
		break;
	case COMPOUND_EXPR:
		check_addr(TREE_OPERAND(exp, 1));
		break;
	case BIND_EXPR:
		check_addr(TREE_OPERAND(exp, 0));
		break;
	default:
		fatal_error("cannot get address of given expression");
	}
}

/* (ID, EXP) */
/* ID=negate,abs,ffs,bit_not,truth_not,non_lvalue,addr */
tree compile_unary(keyword_t op, enum tree_code code, tree *type)
{
	struct term_state ctx;
	tree t, exp;

	init_term(&ctx, op);
	
	exp = compile(get_keyword(&ctx), &t);

	fini_term(&ctx);

	*type = typecheck_unary(op, t);

	if (op == kw_addr)
		check_addr(exp);
	
	return build1(code, *type, exp);
}

/* result() */
tree compile_result(tree *type)
{
	arg0_term(kw_result);
	*type = TREE_TYPE(DECL_RESULT(current_function_decl));
	return DECL_RESULT(current_function_decl);
}

/* ref(ID) */
tree compile_ref(tree *type)
{
	struct arg a;
	struct term_state ctx;
	tree var;

	init_term(&ctx, kw_ref);
	get_term_arg(&ctx, arg_id, &a);
	var = lookup_id(a.u.val_id, VAR_DECL);
	if (var == NULL)
		fatal_error("reference to undefined variable `%s'", 
			    a.u.val_id);

	fini_term(&ctx);

	*type = TREE_TYPE(var);

	return var;
	
}

/* (func_ref ID) */
tree compile_func_ref(tree *type)
{
	struct arg a;
	struct term_state ctx;
	tree var;

	init_term(&ctx, kw_func_ref);
	get_term_arg(&ctx, arg_id, &a);
	var = lookup_id(a.u.val_id, FUNCTION_DECL);
	if (var == NULL)
		fatal_error("reference to undefined function `%s'", 
			    a.u.val_id);

	fini_term(&ctx);

	*type = build_pointer_type(TREE_TYPE(var));

	return build1(ADDR_EXPR, *type, var);
}

/* (component EXP ID) */
tree compile_component(tree *type)
{
	struct term_state ctx;
	tree t, exp, field, ptr;

	init_term(&ctx, kw_component);
	exp = compile_no_kw(&t);
	field = get_id(&ctx);
	fini_term(&ctx);

	if (TREE_CODE(t) != RECORD_TYPE &&
	    TREE_CODE(t) != UNION_TYPE)
		fatal_error("trying to access component of non-(struct|union)");

	for (ptr = TYPE_FIELDS(t); ptr; ptr = TREE_CHAIN(ptr))
		if (TREE_CODE(ptr) == FIELD_DECL &&
		    DECL_NAME(ptr) == field)
		    	break;
	
	if (ptr == NULL)
		fatal_error("%s has no field named %s",
		            TREE_CODE(t) == UNION_TYPE ? "union" : "struct",
			    IDENTIFIER_POINTER(field));
			    
	*type = TREE_TYPE(ptr);

	return build(COMPONENT_REF, *type, exp, ptr);
}

/* var(TYPE, NAME) */
tree compile_var(tree *type, int static_p)
{
	tree name, decl;

	parse_var(type, &name);

	decl = build_decl(VAR_DECL, name, *type);
	TREE_STATIC(decl) = static_p;
	pushdecl(decl);

	if (static_p)
		rest_of_decl_compilation(decl, NULL, 0, 0);

	*type = void_type_node;
	return build1(NOP_EXPR, *type, integer_zero_node);
}

tree compile(keyword_t kw, tree *type)
{
	tree r;

	switch (kw) {
	case kw_const:
		r = compile_const(type);
		break;
	case kw_enumref:
		r = compile_enumref(type);
		break;
	case kw_compound:
		r = compile_compound(type);
                break;
	case kw_bind:
		r = compile_bind(type);
		break;
#ifdef LABELS
	case kw_label:
		r = compile_label(type);
		break;
	case kw_goto:
		r = compile_goto(type);
		break;
#endif
	case kw_return:
		r = compile_return(type);
		break;
	case kw_loop:
		r = compile_loop(type);
		break;
	case kw_exit_loop:
		r = compile_exit_loop(type);
		break;
	case kw_block:
		r = compile_block(type);
		break;
	case kw_exit:
		r = compile_exit(type);
		break;
	case kw_cond:
		r = compile_cond(type);
		break;
	case kw_component:
		r = compile_component(type);
		break;
	case kw_indirect:
		r = compile_indirect(type);
		break;
	case kw_modify:
		r = compile_modify(type);
		break;
	case kw_nop:
		r = compile_nop(type);
		break;
	case kw_ref:
		r = compile_ref(type);
		break;
	case kw_func_ref:
		r = compile_func_ref(type);
		break;
	case kw_result:
		r = compile_result(type);
		break;
	case kw_var:
		r = compile_var(type, 0);
		break;
	case kw_static_var:
		r = compile_var(type, 1);
		break;
	case kw_fbuiltin:
		r = compile_builtin(type, 0);
		break;
	case kw_builtin:
		r = compile_builtin(type, 1);
		break;
	case kw_fcall:
		r = compile_call(type, 0);
		break;
	case kw_call:
		r = compile_call(type, 1);
		break;
	case kw_ptr_fcall:
		r = compile_ptr_call(type, 0);
		break;
	case kw_ptr_call:
		r = compile_ptr_call(type, 1);
		break;
	case kw_convert:
		r = compile_convert(type);
		break;
	case kw_fix_trunc:
		r = compile_fix(type, FIX_TRUNC_EXPR);
		break;
	case kw_fix_ceil:
		r = compile_fix(type, FIX_CEIL_EXPR);
		break;
	case kw_fix_floor:
		r = compile_fix(type, FIX_FLOOR_EXPR);
		break;
	case kw_fix_round:
		r = compile_fix(type, FIX_ROUND_EXPR);
		break;
	case kw_float_expr:
		r = compile_float(type);
		break;
	case kw_wfl:
		r = compile_wfl(type);
		break;
	case kw_size_of:
		r = compile_size_of(type);
		break;
	case kw_try_finally:
		r = compile_try_finally(type);
		break;

	/* prepost */
	case kw_preinc:
		r = compile_prepost(kw_preinc, PREINCREMENT_EXPR, type);
		break;
	case kw_postinc:
		r = compile_prepost(kw_postinc, POSTINCREMENT_EXPR, type);
		break;
	case kw_predec:
		r = compile_prepost(kw_predec, PREDECREMENT_EXPR, type);
		break;
	case kw_postdec:
		r = compile_prepost(kw_postdec, POSTDECREMENT_EXPR, type);
		break;
		
	/* binary expr */
	case kw_minus:
		r = compile_bin(kw_minus, MINUS_EXPR, type);
		break;
	case kw_plus:
		r = compile_bin(kw_plus, PLUS_EXPR, type);
		break;
	case kw_mult:
		r = compile_bin(kw_mult, MULT_EXPR, type);
		break;
	case kw_trunc_div:
		r = compile_bin(kw_trunc_div, TRUNC_DIV_EXPR, type);
		break;
	case kw_ceil_div:
		r = compile_bin(kw_ceil_div, CEIL_DIV_EXPR, type);
		break;
	case kw_floor_div:
		r = compile_bin(kw_floor_div, FLOOR_DIV_EXPR, type);
		break;
	case kw_round_div:
		r = compile_bin(kw_round_div, ROUND_DIV_EXPR, type);
		break;
	case kw_exact_div:
		r = compile_bin(kw_exact_div, EXACT_DIV_EXPR, type);
		break;
	case kw_trunc_mod:
		r = compile_bin(kw_trunc_mod, TRUNC_MOD_EXPR, type);
		break;
	case kw_ceil_mod:
		r = compile_bin(kw_ceil_mod, CEIL_MOD_EXPR, type);
		break;
	case kw_floor_mod:
		r = compile_bin(kw_floor_mod, FLOOR_MOD_EXPR, type);
		break;
	case kw_round_mod:
		r = compile_bin(kw_round_mod, ROUND_MOD_EXPR, type);
		break;
	case kw_bit_ior:
		r = compile_bin(kw_bit_ior, BIT_IOR_EXPR, type);
		break;
	case kw_bit_xor:
		r = compile_bin(kw_bit_xor, BIT_XOR_EXPR, type);
		break;
	case kw_bit_and:
		r = compile_bin(kw_bit_and, BIT_AND_EXPR, type);
		break;
	case kw_bit_andtc:
		r = compile_bin(kw_bit_andtc, BIT_ANDTC_EXPR, type);
		break;
	case kw_lshift:
		r = compile_bin(kw_lshift, LSHIFT_EXPR, type);
		break;
	case kw_rshift:
		r = compile_bin(kw_rshift, RSHIFT_EXPR, type);
		break;
	case kw_lrotate:
		r = compile_bin(kw_lrotate, LROTATE_EXPR, type);
		break;
	case kw_rrotate:
		r = compile_bin(kw_rrotate, RROTATE_EXPR, type);
		break;
	case kw_unlt:
		r = compile_bin(kw_unlt, UNLT_EXPR, type);
		break;
	case kw_unle:
		r = compile_bin(kw_unle, UNLE_EXPR, type);
		break;
	case kw_ungt:
		r = compile_bin(kw_ungt, UNGT_EXPR, type);
		break;
	case kw_unge:
		r = compile_bin(kw_unge, UNGE_EXPR, type);
		break;
	case kw_uneq:
		r = compile_bin(kw_uneq, UNEQ_EXPR, type);
		break;
	case kw_ordered:
		r = compile_bin(kw_ordered, ORDERED_EXPR, type);
		break;
	case kw_unordered:
		r = compile_bin(kw_unordered, UNORDERED_EXPR, type);
		break;
	case kw_rdiv:
		r = compile_bin(kw_rdiv, RDIV_EXPR, type);
		break;
	case kw_truth_andif:
		r = compile_bin(kw_truth_andif, TRUTH_ANDIF_EXPR, type);
		break;
	case kw_truth_orif:
		r = compile_bin(kw_truth_orif, TRUTH_ORIF_EXPR, type);
		break;
	case kw_truth_and:
		r = compile_bin(kw_truth_and, TRUTH_AND_EXPR, type);
		break;
	case kw_truth_or:
		r = compile_bin(kw_truth_or, TRUTH_OR_EXPR, type);
		break;
	case kw_truth_xor:
		r = compile_bin(kw_truth_xor, TRUTH_XOR_EXPR, type);
		break;
	case kw_lt:
		r = compile_bin(kw_lt, LT_EXPR, type);
		break;
	case kw_le:
		r = compile_bin(kw_le, LE_EXPR, type);
		break;
	case kw_gt:
		r = compile_bin(kw_gt, GT_EXPR, type);
		break;
	case kw_ge:
		r = compile_bin(kw_ge, GE_EXPR, type);
		break;
	case kw_min:
		r = compile_bin(kw_min, MIN_EXPR, type);
		break;
	case kw_max:
		r = compile_bin(kw_max, MAX_EXPR, type);
		break;
	case kw_eq:
		r = compile_bin(kw_eq, EQ_EXPR, type);
		break;
	case kw_ne:
		r = compile_bin(kw_ne, NE_EXPR, type);
		break;

	/* unary expressions */
	case kw_negate:
		r = compile_unary(kw_negate, NEGATE_EXPR, type);
		break;
	case kw_abs:
		r = compile_unary(kw_abs, ABS_EXPR, type);
		break;
	case kw_ffs:
		r = compile_unary(kw_ffs, FFS_EXPR, type);
		break;
	case kw_bit_not:
		r = compile_unary(kw_bit_not, BIT_NOT_EXPR, type);
		break;
	case kw_truth_not:
		r = compile_unary(kw_truth_not, TRUTH_NOT_EXPR, type);
		break;
	case kw_non_lvalue:
		r = compile_unary(kw_non_lvalue, NON_LVALUE_EXPR, type);
		break;
	case kw_addr:
		r = compile_unary(kw_addr, ADDR_EXPR, type);
		break;

	/* oops... */
	default:
		fatal_error("unexpected (%s ...)", keyword_name(kw));
		r = NULL;
		break;
	}

	r = fold(r);

	return r;
}

tree compile_no_kw(tree *type)
{
	struct arg a;
	
	get_arg(arg_kw, &a);
	return compile(a.u.val_kw, type);
}

/* Return nonzero if REF is an lvalue valid for this language.
   Lvalues can be assigned, unless their type has TYPE_READONLY.
   Lvalues can have their address taken, unless they have DECL_REGISTER.  */

int lvalue_p(tree ref)
{
	switch (TREE_CODE(ref)) {
	case REALPART_EXPR:
	case IMAGPART_EXPR:
	case COMPONENT_REF:
	case EXPR_WITH_FILE_LOCATION:
		return lvalue_p(TREE_OPERAND(ref, 0));

	case STRING_CST:
		return 1;

	case INDIRECT_REF:
	case ARRAY_REF:
	case VAR_DECL:
	case PARM_DECL:
	case RESULT_DECL:
	case ERROR_MARK:
		return (TREE_CODE(TREE_TYPE(ref)) != FUNCTION_TYPE
			&& TREE_CODE(TREE_TYPE(ref)) != METHOD_TYPE);

	case BIND_EXPR:
	case RTL_EXPR:
		return TREE_CODE(TREE_TYPE(ref)) == ARRAY_TYPE;

	default:
		return 0;
	}
}
