static char rcsid[] = "$Id: C-compile-expr.c,v 1.44 2002/02/16 20:56:53 msato Exp $";
/* 
 * $RWC_Release: Omni-1.6 $
 * $RWC_Copyright:
 *  Omni Compiler Software Version 1.5-1.6
 *  Copyright (C) 2002 PC Cluster Consortium
 *  
 *  This software is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU Lesser General Public License version
 *  2.1 published by the Free Software Foundation.
 *  
 *  Omni Compiler Software Version 1.0-1.4
 *  Copyright (C) 1999, 2000, 2001.
 *   Tsukuba Research Center, Real World Computing Partnership, Japan.
 *  
 *  Please check the Copyright and License information in the files named
 *  COPYRIGHT and LICENSE under the top  directory of the Omni Compiler
 *  Software release kit.
 *  
 *  
 *  $
 */
/* Compile statements and expressions */
#include "C-front.h"

static expv 	cons_compound_statement _ANSI_ARGS_((expv w, list decl_lp, ID id_list));
static void 	check_args_with_prototype _ANSI_ARGS_((expv params, expv args, expr x));

/* compound_statement := (LIST decl statement-list) */
expv    compile_compound_statement(expr x)
{
    list    lp;
    expv    v, w, decl;

    PUSH_ENV;

	/* local declarations */
    decl = compile_declaration_list(EXPR_ARG1(x), AUTO);

    if (debug_X_flag){
	fprintf(debug_fp, "====compound statement declarations====\n");
	dump_id_list(current_level);
	fprintf(debug_fp, "=======================================\n");
    }

    w = list0(LIST);	/* list node for return */
    FOR_ITEMS_IN_LIST(lp, EXPR_ARG2(x)) {
	v = LIST_ITEM(lp);
	v = compile_statement(v);
	if (v == NULL) continue;
	w = list_put_last(w, v);
    }

    /* list (id-list, decl, statement) */
    w = cons_compound_statement(w,EXPR_LIST(decl),CURRENT_ID_LIST);

    POP_ENV;

    return w;
}

/* split context hided by auto variable */
static expv cons_compound_statement(expv w,list decl_lp,ID id_list)
{
    ID		idp,idq;
    TYPE_DESC	tp;
    expv	decls,v;
    int		have_init = FALSE;

    decls = list0(LIST);
    idq = NULL;
    for(idp = id_list; idp != NULL && decl_lp != NULL;
	idq = idp, idp = ID_NEXT(idp)){
        tp = ID_TYPE(idp);
	if(idq != NULL  &&  have_init  &&
	   (ID_HIDE(idp) || TYPE_IS_CONST(tp))) {
	    ID_NEXT(idq) = NULL;
	    w = cons_compound_statement(w,decl_lp,idp);
	    decl_lp = NULL;
	    break;
	}
	v = LIST_ITEM(decl_lp);
	if((EXPR_SYM(EXPR_ARG1(v)) == ID_SYM(idp))) {
	    list_put_last(decls,v);
	    decl_lp = LIST_NEXT(decl_lp);
	    if(EXPR_CODE(v) == VAR_DECL  &&  EXPR_ARG2(v) != NULL  &&
	       !(TYPE_IS_CONST(tp))) {
		have_init = TRUE;
	    }
	}
    }
    /* if(decl_lp != NULL) fatal("cons_compound_statement: bad decls?"); */
    return list3(COMPOUND_STATEMENT, 
		 expv_any_term(ID_LIST, (void *)id_list),
		 decls, w);
}

/* statement analysis */
expv  compile_statement(expr x)
{
    expv    v, v1, v2, w = NULL;	/* w: compiled IR */
    ID      id, id_list;
    list    lp;

    if ( x == NULL )	/* for NULL statement */
	return NULL;

    if (debug_expr_flag) {
	fprintf(debug_fp, "### --- compile_statement input  ------\n");
	expr_print(x, debug_fp);
	fprintf(debug_fp, "###------------------------------------\n");
    }

    switch (EXPR_CODE(x)) {
    case COMPOUND_STATEMENT:
	w = compile_compound_statement(x);
	break;

    case PRAGMA_LINE:
	w = compile_pragma_line(x);
	break;

    case LIST:			/* (LIST statement ....) */
	w = list0(LIST);
	FOR_ITEMS_IN_LIST(lp, x){
	    v = LIST_ITEM(lp);
	    v = compile_statement(v);
	    if (v == NULL) continue;
	    w = list_put_last(w, v);
	}
	break;

    case IF_STATEMENT:		/* (IF_STATMENT cond then-part else-part) */
	v = compile_logical_expression(EXPR_ARG1(x));
	v1 = compile_statement(EXPR_ARG2(x));	/* then part */
	if (EXPR_ARG3(x))	/* else part(if exist) */
	    v2 = compile_statement(EXPR_ARG3(x));
	else
	    v2 = NULL;
	w = list3(IF_STATEMENT, v, v1, v2);
	break;

    case WHILE_STATEMENT:	/* (WHILE_STATEMENT cond body) */
	v = compile_logical_expression(EXPR_ARG1(x));
	/* set current evniroment */
	break_level++;
	continue_level++;
	v1 = compile_statement(EXPR_ARG2(x));
	break_level--;
	continue_level--;	/* OUT */
	w = list2(WHILE_STATEMENT, v, v1);
	break;

    case FOR_STATEMENT:	/* (FOR init cond iter body) */
	v = compile_expression(EXPR_ARG1(x));	/* init */
	v1 = compile_logical_expression(EXPR_ARG2(x));	/* cond */
	v2 = compile_expression(EXPR_ARG3(x));	/* iter */
	break_level++;
	continue_level++;
	w = compile_statement(EXPR_ARG4(x));	/* body */
	break_level--;
	continue_level--;	/* OUT */
	w = list4(FOR_STATEMENT, v, v1, v2, w);
	break;

    case DO_STATEMENT:		/* (DO_STATEMENT body cond) */
	break_level++;
	continue_level++;
	w = compile_statement(EXPR_ARG1(x));
	v = compile_logical_expression(EXPR_ARG2(x));
	break_level--;
	continue_level--;	/* OUT */
	w = list2(DO_STATEMENT, w, v);
	break;

    case BREAK_STATEMENT:	/* (BREAK_STATEMENT) */
	if ( break_level == 0 ){
	    error_at_node(x, "illegal break statement");
	    break;
	}
	w = list0(BREAK_STATEMENT);
	break;

    case CONTINUE_STATEMENT:	/* (CONTINUE_STATEMENT) */
	if ( continue_level == 0 ){
	    error_at_node(x, "illegal continue statement");
	    break;
	}
	w = list0(CONTINUE_STATEMENT);
	break;

    case GOTO_STATEMENT:	/* (GOTO_STATEMENT label) */
	id = define_ident(EXPR_ARG1(x), ULABEL, NULL);
	v1 = expv_sym_term(IDENT, NULL, ID_SYM(id));
	w = list1(GOTO_STATEMENT, v1);
	break;

    case STATEMENT_LABEL:	/* (STATEMENT_LABEL label_ident) */
	id = define_ident(EXPR_ARG1(x), LABEL, NULL);
	v1 = expv_sym_term(IDENT, NULL, ID_SYM(id));
	w = list1(STATEMENT_LABEL, v1);
	break;

    case SWITCH_STATEMENT:	/* (SWITCH_STATEMENT value body) */
	v = compile_expression(EXPR_ARG1(x));	/* compile switch value */
	if ( !EXPV_IS_INTEGRAL(v) )
	    error_at_node(x, "switch quantity not an integer");
	break_level++;
	switch_labels[switch_level] = NULL;
	switch_level++;
	w = compile_statement(EXPR_ARG2(x));
	switch_level--;
	break_level--;
	w = list2(SWITCH_STATEMENT, v, w);
	break;

    case CASE_LABEL:	/* (CASE_LABEL value) */
	v = compile_expression(EXPR_ARG1(x));
	v = expv_reduce(v);
	if ( EXPV_CODE(v) != INT_CONSTANT && EXPV_CODE(v) != MOE_CONSTANT ){
	    error_at_node(x, "case expression must be an integer constant");
	    break;
	}
	if ( switch_level == 0 ){
	    error_at_node(x, "case not in switch");
	    break;
	}
	id = new_ident_desc(NULL);
	ID_CASE_EXPV(id) = v;
	id_list = switch_labels[switch_level - 1];
	if ( id_list == NULL )
	    switch_labels[switch_level - 1] = id;
	else {
	    while(1) {
		if ( ID_CASE_EXPV(id_list) != NULL &&
			EXPV_INT_VALUE(ID_CASE_EXPV(id_list)) == EXPV_INT_VALUE(v) ){
		    error_at_node(x, "duplicate switch, %d", EXPV_INT_VALUE(v));
		    goto ret;
		}
		if (ID_NEXT(id_list) == NULL)
		    break;
		id_list = ID_NEXT(id_list);
	    }
	    ID_NEXT(id_list) = id;
	}
	w = list1(CASE_LABEL, v);
	break;

    case DEFAULT_LABEL:	/* (DEFAULT_LABEL) */
	if ( switch_level == 0 ){
	    error_at_node(x, "default label not in switch");
	    break;
	}
	id = new_ident_desc(NULL);
	ID_CASE_EXPV(id) = NULL;
	id_list = switch_labels[switch_level - 1];
	if ( id_list == NULL )
	    switch_labels[switch_level - 1] = id;
	else {
	    while(1) {
		if (ID_CASE_EXPV(id_list) == NULL) {
		    error_at_node(x, "duplicate default label");
		    goto ret;
		}
		if ( ID_NEXT(id_list) == NULL )
		    break;
		id_list = ID_NEXT(id_list);
	    }
	    ID_NEXT(id_list) = id;
	}
	w = list0(DEFAULT_LABEL);
	break;

    case RETURN_STATEMENT:	/* (RETURN_STATEMENT value) */
	if (EXPR_ARG1(x)) {
	    v = compile_expression(EXPR_ARG1(x));
	    if (v == NULL)
		break;		/* error recovery */
	    if ( IS_VOID(return_type) ){
		error_at_node(x, "void function cannot return value");
		break;
	    }
	    if ( !check_assignment_type(return_type, v, x, "return") )
		break;
	    if ( IS_STRUCT(return_type) || IS_UNION(return_type) ){
		if (TYPE_SIZE(return_type) == 0) {
		    error_at_node(x, "return size unknown");
		    break;
		}
	    }
	}
	else
	    v = NULL;
	w = list1(RETURN_STATEMENT, v);
	break;

default:	/* just expression */
	v = compile_expression(x);
	w = list1(EXPR_STATEMENT, v);
    }

ret:
    if (w){
	EXPR_LINE(w) = EXPR_LINE(x); /* keep line number info */

	if (debug_X_flag){
	    fprintf(debug_fp, "---- compound statement internal expr ----\n");
	    X_output(w, debug_fp);
	    fprintf(debug_fp, "------------------------------------------\n");
	}
    }

    return w;
}

/* compile * (base) = init */
expv compile_init_assignment(TYPE_DESC tp, expr init, int  must_const)
{
    expv    v, u;

    v = compile_expression(init);
    if ( v == NULL ) return NULL;	/* error */

	/* tp and TYPE(v) check, last 2 args are for messages */
    if ( !check_assignment_type(tp, v, init, "assignment") )
	return NULL;

    if ( must_const ){
	u = expv_type_conversion(tp, v);
	v = expv_reduce(u);

	if(!expv_is_constant (v)  &&  !expv_is_addr (v)) {
	    error_at_node(init, "illegal initialization");
	}
    }

    return v;
}

int expv_is_constant(expv v)
{
    switch(EXPV_CODE(v)){
    case INT_CONSTANT:
    case LONG_CONSTANT:
    case LONGLONG_CONSTANT:
    case STRING_CONSTANT:
    case FLOAT_CONSTANT:
    case MOE_CONSTANT:
    case VAR_ADDR:
    case FUNC_ADDR:
	return TRUE;
    case MEMBER_ADDR:
    case CAST_EXPR:
	return expv_is_constant(EXPV_LEFT(v));
    default:
	return FALSE;
    }
}

int
expv_is_addr (expv v)
{
    switch (EXPV_CODE(v)) {
    case VAR_ADDR:
    case PARAM_ADDR:
    case LVAR_ADDR:
    case LARRAY_ADDR:
    case ARRAY_ADDR:
    case FUNC_ADDR:
    case MEMBER_ADDR:
    case MEMBER_ARRAY_ADDR:
	return TRUE;
    case CAST_EXPR:
	return expv_is_addr (EXPV_LEFT(v));
    case PLUS_EXPR:
	if (expv_is_addr(EXPV_LEFT(v)) == TRUE  &&  expv_is_constant(EXPV_RIGHT(v))) {
	    return TRUE;
	} else {
	    return FALSE;
	}
    default:
	return FALSE;
    }
}

/*
 * evaluate expression.
 */
expv    compile_expression(expr    x)
{
    ID      id = NULL;
    expv    v, right, left, res0, res1, res2;
    TYPE_DESC tp = NULL;
    TYPE_DESC tq;
    expr    member;
    list    lp;
    int     asg_op = FALSE;	/* '+=' type operation */

    if (x == NULL)
	return (NULL);

    switch (EXPR_CODE(x)) {
    case ERROR_NODE:
	return (NULL);

	/* teminal */
    case IDENT:
	id = lookup_ident(x);
	if ( id == NULL ){
	    error_at_node(x, "undefined variable, %s", SYM_NAME(EXPR_SYM(x)));
	    id = define_ident(x, SNULL, default_data_type);
	}
	switch (ID_CLASS(id)) {
	case SNULL:
	    return NULL;	/* error recovery */

	case TYPEDEF_NAME:
	    fatal("typename appears as identifier");
	    break;

	case LABEL:
	case ULABEL:
	    error_at_node(x, "label is referenced as variable, %s",
			  SYM_NAME(EXPR_SYM(x)));
	    return NULL;

	case MOE:
	    res1 = expv_sym_term(MOE_CONSTANT, ID_TYPE(id), ID_SYM(id));
	    return (res1);

	default:
	    break;
	}

	if ( ID_BASE(id) == NULL )
	    return (NULL);	/* error recovery */

	v = ID_BASE(id);
	tp = ID_TYPE(id);
	if ( IS_FUNCTION(tp) ){
	    /* res1 =  expv_retype(pointer_type(tp), v); */
	    res1 =  expv_retype(tp, v);
	    return (res1);
	} else if (IS_ARRAY(tp) && EXPR_CODE(v) != PARAM_ADDR){
	    res0 =  expv_retype(pointer_type(TYPE_REF(tp)), v);
	    res1 =  res0 /*expv_cons(POINTER_REF, tp, res0, NULL)*/;
	    return (res1);
	} else{
	    res0 = expv_retype(pointer_type(tp), v);
	    res1 = expv_cons(POINTER_REF, tp, res0, NULL);
	    return (res1);
	}
	/* NOT REACHED HARE*//* break; */

    case STRING_CONSTANT:
	res1 = expv_str_term(string_type, EXPR_STR(x));
	return (res1);

    case INT_CONSTANT:
	res1 = expv_int_term(INT_CONSTANT, int_type, EXPR_INT(x));
	return (res1);

    case LONG_CONSTANT:
	res1 = expv_long_term(LONG_CONSTANT, long_type, EXPR_LINT(x));
	return (res1);

    case FLOAT_CONSTANT:
	res1 = expv_float_term(float_type, EXPR_FLOAT(x));
	return (res1);

    case LONGLONG_CONSTANT:
	res1 = expv_longlong_term(longlong_type, EXPR_LLINT(x));
	return (res1);

	/* primary expr */
    case POINTER_REF:		/* (POINTER_REF x) */
	v = compile_expression(EXPR_ARG1(x));
	if ( v == NULL )
	    goto err;
	tp = EXPV_TYPE(v);
	if ( IS_ARRAY(tp) || IS_POINTER(tp) )
	    tp = TYPE_REF(tp);
	else {
	    error_at_node(x, "illegal indirection");
	    goto err;
	}

    pointer_ref:
	if ( IS_ARRAY(tp) ){
	    res1 = expv_cons(ARRAY_AREF, pointer_type(TYPE_REF(tp)), v, NULL);
	    return(res1);
	}
	else{
	    res1 = expv_cons(POINTER_REF, tp, v, NULL);
	    return (res1);
	}

    case ARRAY_REF:		/* (ARRAY_REF x n) */
	left = compile_expression(EXPR_ARG1(x));
	right = compile_expression(EXPR_ARG2(x));
	if (left == NULL || right == NULL)
	    goto err;
	if (!EXPV_IS_POINTER(left)){
	    error_at_node(x, "illegal array expression");
	    goto err;
	}
	if (!EXPV_IS_INTEGRAL(right)){
	    error_at_node(x, "subscripted value is neither array nor pointer");
	    goto err;
	}
	tp = EXPV_TYPE(left);
	v = expv_cons(PLUS_EXPR,tp,left,right);
	tp = TYPE_REF(tp);
	if(IS_ARRAY(tp) && TYPE_ARRAY_DIM(tp) == 0){
	    error_at_node(x,"reference to array with unspecified bounds");
	}
	goto pointer_ref;

#ifdef not
	res1 = expv_cons(ARRAY_REF, TYPE_REF(EXPV_TYPE(left)), left, right);
	return (res1);
#endif
#ifdef not
	/* convert p[n] => *(p+n) */
	res0 = list2(PLUS_EXPR, EXPR_ARG1(x), EXPR_ARG2(x));
	EXPR_LINE(res0) = EXPR_LINE(x);
	res0 = list1(POINTER_REF, res0);
	EXPR_LINE(res0) = EXPR_LINE(x);
	res1 = compile_expression(res0);
	return (res1);
#endif

    case STRUCT_REF:		/* (STRUCT_REF v member), v.member */
	v = compile_expression(EXPR_ARG1(x));
	if ( v == NULL )
	    goto err;
	tp = EXPV_TYPE(v);
	if ( TYPE_DESC_CODE(tp) == STRUCT_TYPE || TYPE_DESC_CODE(tp) == UNION_TYPE )
	     /* OK */ ;
	else {
	    error_at_node(x, "struct/union or struct/union pointer required");
	    goto err;
	}
	member = EXPR_ARG2(x);
	for( id = TYPE_MEMBER_LIST(tp) ; id != NULL ; id = ID_NEXT(id) )
	    if ( ID_SYM(id) == EXPR_SYM(member) )
		break;
	if ( id == NULL ){
	    error_at_node(x, "structure/union has no member named `%s'",
			  SYM_NAME(EXPR_SYM(member)) );
	    goto err;
	}
	tp = ID_TYPE(id);	/* member type */

	if ( EXPV_CODE(v) != POINTER_REF ){
	    if(EXPR_CODE(v) == FUNCTION_CALL)
		return expv_cons(MEMBER_REF,tp,v,
				 expv_sym_term(IDENT, tp, ID_SYM(id)));
	    fatal("compile_expression: STRUCT_REF");
	}

	if ( IS_ARRAY(tp) ){
	    res0 = expv_sym_term(IDENT, tp, ID_SYM(id));
	    res1 = expv_cons(MEMBER_ARRAY_ADDR, tp, EXPV_LEFT(v), res0);
	    return (res1);
	}
	else {			/* get address */
	    res2 = expv_sym_term(IDENT, tp, ID_SYM(id));
	    res0 = expv_cons(MEMBER_ADDR, pointer_type(tp), EXPV_LEFT(v), res2);
	    res1 = expv_cons(POINTER_REF, tp, res0 ,NULL);
	    return (res1);
	}
	/* no one reach here */

    case ADDR_OF:	/* & operator */
	v = compile_expression(EXPR_ARG1(x));
	if ( v == NULL )
	    goto err;
	tp = EXPV_TYPE(v);
	switch(EXPV_CODE(v)){
	case POINTER_REF:
	    res1 = expv_retype(pointer_type(tp), EXPV_LEFT(v));
	    return (res1);
	case ARRAY_ADDR:
	case LARRAY_ADDR:
	case MEMBER_ARRAY_ADDR:
	    /* warning ? */
	    return v;
	default:
	    break;
	}

	if(IS_FUNCTION(tp) || IS_ARRAY(tp)){
	  warning_at_node(x, "& before array or function: ignored");
	  return v;
	}
	error_at_node(x, "unacceptable operand of &");
	goto err;

    case COMMA_EXPR:
	left = compile_expression(EXPR_ARG1(x));
	right = compile_expression(EXPR_ARG2(x));
	if (left == NULL || right == NULL)
	    goto err;

	/* take the right operand type */
	res1 = expv_cons(COMMA_EXPR, EXPV_TYPE(right), left, right);
	return (res1);

	/* assignment operator */
    case ASSIGN_EXPR:
	left = compile_lvalue_expression(EXPR_ARG1(x));
	right = compile_expression(EXPR_ARG2(x));
	if (left == NULL || right == NULL)
	    goto err;
	tp = EXPV_TYPE(left);
	if ( !check_assignment_type(tp, right, x, "assignemnt") )
	    goto err;
	/* default */
	res0 = expv_type_conversion(tp, right);
	res1 = expv_cons(ASSIGN_EXPR, tp, left, res0);
	return (res1);

	/* arithmetic additive operator */
    case ASG_PLUS_EXPR:
    case ASG_MINUS_EXPR:
	asg_op = TRUE;
	left = compile_lvalue_expression(EXPR_ARG1(x));
	right = compile_expression(EXPR_ARG2(x));
	if ( left == NULL || right == NULL )
	    goto err;
	goto additive_binary_op;

    case PLUS_EXPR:
    case MINUS_EXPR:
	left = compile_expression(EXPR_ARG1(x));
	right = compile_expression(EXPR_ARG2(x));
	if ( left == NULL || right == NULL )
	    goto err;
	if (EXPR_CODE(x) == PLUS_EXPR &&
		!EXPV_IS_POINTER(left) && EXPV_IS_POINTER(right)) {
	    v = left;	/* swap, left is POINTER */
	    left = right;
	    right = v;
	}
    additive_binary_op:
	if ( EXPV_IS_POINTER(left) ){
	    tp = EXPV_TYPE(left);
	    /* pointer scaling, left must be interger. */
	    if ( IS_INTEGRAL(EXPV_TYPE(right)) ){	/* no scaling */
		res1 = expv_cons(EXPR_CODE(x), tp, left, right);
		return (res1);
	    }
	    else if ( EXPV_IS_POINTER(right) && EXPR_CODE(x) == MINUS_EXPR ){
		/* pointer minus, no scaling */
		if ( type_equal(EXPV_TYPE(left), EXPV_TYPE(right)) ){
		    res1 = expv_cons(MINUS_EXPR, int_type, left, right);
		    return (res1);
		}
		error_at_node(x, "illegal pointer subtraction");
		return NULL;
	    }
	    goto invalid_operand;
	}
		/* otherwise, arithmetic operation */
	tp = arithmetic_operator_type(left, right);
	if ( tp == NULL )
	    goto invalid_operand;
	goto binary_op;

    case ASG_DIV_EXPR:
    case ASG_MUL_EXPR:
	asg_op = TRUE;
	left = compile_lvalue_expression(EXPR_ARG1(x));
	goto arithmetic_binary_op;

    case DIV_EXPR:
    case MUL_EXPR:
	left = compile_expression(EXPR_ARG1(x));
arithmetic_binary_op:
	right = compile_expression(EXPR_ARG2(x));
	if (left == NULL || right == NULL)
	    goto err;
	/* interger or float */
	tp = arithmetic_operator_type(left, right);
	if ( tp == NULL )
	    goto invalid_operand;
	goto binary_op;

    case UNARY_MINUS_EXPR:
	v = compile_expression(EXPR_ARG1(x));
	if ( v == NULL )
	    goto err;
	tp = arithmetic_operator_type(v, NULL);
	if ( tp == NULL )
	    goto invalid_operand;
	goto unary_op;

    case ASG_MOD_EXPR:
    case ASG_BIT_AND_EXPR:
    case ASG_BIT_OR_EXPR:
    case ASG_BIT_XOR_EXPR:
	asg_op = TRUE;
	left = compile_lvalue_expression(EXPR_ARG1(x));
	goto integral_binary_op;

    case MOD_EXPR:
    case BIT_AND_EXPR:
    case BIT_OR_EXPR:
    case BIT_XOR_EXPR:
	left = compile_expression(EXPR_ARG1(x));
integral_binary_op:
	right = compile_expression(EXPR_ARG2(x));
	if (left == NULL || right == NULL)
	    goto err;
	/* interger or float */
	tp = integral_operator_type(left, right);
	if ( tp == NULL )
	    goto invalid_operand;
	goto binary_op;

    case BIT_NOT_EXPR:
	v = compile_expression(EXPR_ARG1(x));
	if ( v == NULL )
	    goto err;
	tp = integral_operator_type(v, NULL);
	if ( tp == NULL )
	    goto invalid_operand;
	goto unary_op;

	/* shift operation */
    case ASG_LSHIFT_EXPR:
    case ASG_RSHIFT_EXPR:
	asg_op = TRUE;
	left = compile_lvalue_expression(EXPR_ARG1(x));
	goto shift_binary_op;

    case LSHIFT_EXPR:
    case RSHIFT_EXPR:
	left = compile_expression(EXPR_ARG1(x));
shift_binary_op:
	right = compile_expression(EXPR_ARG2(x));
	if ( left == NULL || right == NULL )
	    goto err;
	if ( !EXPV_IS_INTEGRAL(left) || !EXPV_IS_INTEGRAL(right) )
	    goto invalid_operand;
	res0 = expv_type_conversion(int_type, right);
	res1 = expv_cons(EXPR_CODE(x), EXPV_TYPE(left), left, res0);
	return (res1);

	/* relational and equality operator!!??? */
    case LOG_GE_EXPR:
    case LOG_GT_EXPR:
    case LOG_LE_EXPR:
    case LOG_LT_EXPR:
    case LOG_EQ_EXPR:
    case LOG_NEQ_EXPR:
	left = compile_expression(EXPR_ARG1(x));
	right = compile_expression(EXPR_ARG2(x));
	if (left == NULL || right == NULL)
	    goto err;
	/* arithmetic type is allowed */
	tp = arithmetic_operator_type(left, right);
	if ( tp != NULL )
	    goto log_binary_op;

	/* or, pointers to compatible object */
	if ( EXPV_IS_POINTER(left) && EXPV_IS_ZERO(right) ){
	    tp = EXPV_TYPE(left);
	    goto log_binary_op;
	}
	if ( EXPV_IS_ZERO(left) && EXPV_IS_POINTER(right) ){
	    tp = EXPV_TYPE(right);
	    goto log_binary_op;
	}
	if ( EXPV_IS_POINTER(left) && EXPV_IS_POINTER(right) ){
	    if ( !is_compatible_type(EXPV_TYPE(left), EXPV_TYPE(right)) )
		warning_at_node(x, "comparison of incompatible type pointer");
	    tp = NULL;
	    goto log_binary_op;
	}
	if(EXPV_IS_POINTER(right) && EXPV_IS_FUNCTION(left)) {
	    left = expv_retype(pointer_type(EXPV_TYPE(left)), left);
	    if ( !is_compatible_type(EXPV_TYPE(left), EXPV_TYPE(right)) )
		warning_at_node(x, "comparison of incompatible type pointer");
	    tp = NULL;
	    goto log_binary_op;
	}
	if(EXPV_IS_FUNCTION(right) && EXPV_IS_POINTER(left)) {
	    right = expv_retype(pointer_type(EXPV_TYPE(right)), right);
	    if ( !is_compatible_type(EXPV_TYPE(left), EXPV_TYPE(right)) )
		warning_at_node(x, "comparison of incompatible type pointer");
	    tp = NULL;
	    goto log_binary_op;
	}
	if(EXPV_IS_INTEGRAL(left) && EXPV_IS_POINTER(right)){
	    tp = EXPV_TYPE(left);
	    warning_at_node(x,"comparison between pointer and interger");
	    goto log_binary_op;
	}
	if(EXPV_IS_INTEGRAL(right) && EXPV_IS_POINTER(left)){
	    tp = EXPV_TYPE(right);
	    warning_at_node(x,"comparison between pointer and interger");
	    goto log_binary_op;
	}
	goto invalid_operand;

	/* logical operator */
    case LOG_AND_EXPR:
    case LOG_OR_EXPR:
	left = compile_expression(EXPR_ARG1(x));
	right = compile_expression(EXPR_ARG2(x));
	if ( left == NULL || right == NULL )
	    goto err;
	tp = NULL;
	if ( EXPV_IS_SCALAR(left) && EXPV_IS_SCALAR(right) )
	    goto log_binary_op;
	goto invalid_operand;

	/* logical not */
    case LOG_NOT_EXPR:
	v = compile_expression(EXPR_ARG1(x));
	if ( v == NULL )  goto err;
	if ( !EXPV_IS_SCALAR(v) ) goto invalid_operand;
	res1 = expv_cons(LOG_NOT_EXPR, int_type, v, NULL);
	return (res1);

    case FUNCTION_CALL:	/* (FUNCTION_CALL function args-list) */
	/* check function defined or not */
	if ( EXPR_CODE(EXPR_ARG1(x)) == IDENT && 
	     lookup_ident(EXPR_ARG1(x)) == NULL ){
	    /* if not defined, declare function as default. */
	    define_ident(EXPR_ARG1(x), EXTERN, default_func_type);
	}
	left = compile_expression(EXPR_ARG1(x));	/* function */
	if(left == NULL) goto err; /* error recovery */
	tp = EXPV_TYPE(left);
	if(IS_FUNCTION(tp)) {
	    left = expv_retype(pointer_type(tp), left);
	}

	tp = NULL;	/* for error check */
	tq = NULL;
	if (left != NULL) {	/* check function type */
	    tp = EXPV_TYPE(left);
	    if ( IS_POINTER(tp) )
		tp = TYPE_REF(tp);
	    else if ( IS_FUNCTION(tp) ) { /* func itself, remove indirection */
		if ( EXPV_CODE(left) == POINTER_REF )
		    left = EXPV_LEFT(left);
		else
		    left = NULL;/* error */
	    }
	    else
		left = NULL;	/* error */

	    /* finally, it must be function */
	    if ( IS_FUNCTION(tp) ){
		tq = tp;	/* function type */
		tp = TYPE_REF(tp);
	    } else
		left = NULL;	/* error */
	}

	/* evaluate arugment */
	right = NULL;
	if ( EXPR_ARG2(x) ){
	    right = list0(LIST);
	    /* evaluate argument */
	    FOR_ITEMS_IN_LIST(lp, EXPR_ARG2(x)) {
		v = compile_expression(LIST_ITEM(lp));
		right = list_put_last(right, v);
	    }
	}
	if(tq != NULL && TYPE_IS_FUNC_PROTO(tq))
	    check_args_with_prototype(TYPE_FUNC_PARAMS(tq),right,x);
	
	if (tp == NULL)
	    goto err;		/* error recovery */

	/* check function */
	if (left == NULL) {
	    if (tp != NULL)
		error_at_node(x, "illegal function");
	    goto err;
	}
	res1 = expv_cons(FUNCTION_CALL, tp, left, right);
	return (res1);

    case POST_INCR_EXPR:
    case POST_DECR_EXPR:
	left = compile_expression(EXPR_ARG1(x));
	if ( left == NULL )
	    goto err;
	if ( EXPV_CODE(left) != POINTER_REF ){
	    error_at_node(x, "illegal lvalue in %s",
		EXPR_CODE(x) == POST_INCR_EXPR ? "increment" : "decrement");
			 
	    goto err;
	}
	tp = EXPV_TYPE(left);
	if ( IS_ARITHMETIC(tp) || IS_POINTER(tp) ){
	    res1 = expv_cons(EXPR_CODE(x), tp, left, NULL);
	    return (res1);
	}
	else {	/* not scalar type */
	    error_at_node(x, "invalid type operand in %s",
		EXPR_CODE(x) == POST_INCR_EXPR ? "increment" : "decrement");
			 
	}
	goto err;

    case SIZE_OF_EXPR:		/* (SIZE_OF_EXPR type-or-expr) */
	/* check type or expr */
	switch (EXPR_CODE(EXPR_ARG1(x))) {
	case BASIC_TYPE_NODE:
	case LIST:
	case ENUM_TYPE:
	case STRUCT_TYPE:
	case UNION_TYPE:	/* type_name */
	    tp = compile_decl_type_spec(EXPR_ARG1(EXPR_ARG1(x)));
	    if (EXPR_ARG2(EXPR_ARG1(x))) /* combine abstract declarator */
		tp = compile_declarator_type(EXPR_ARG2(EXPR_ARG1(x)), tp);
	    break;

	default:
	    v = compile_expression(EXPR_ARG1(x));
	    if ( v == NULL ) goto err;
	    tp = EXPV_TYPE(v);
	    if(IS_POINTER(tp)){
		/* if pointer_type, compile_expression may change A to P */
		x = EXPR_ARG1(x);
		if(EXPR_CODE(x) == IDENT){ /* type directly from IDENT */
		    id = lookup_ident(x);
		    tp = ID_TYPE(id);
		} else if (EXPR_CODE(v) == ARRAY_AREF){
		    tp = TYPE_REF(EXPV_TYPE(EXPV_LEFT(v)));
		} else if (EXPR_CODE(x) == STRING_CONSTANT) {
		    res1 = expv_int_term (INT_CONSTANT, int_type, strlen (EXPV_STR(x)) + 1);
		    return res1;
		}
	    }
	    break;
	}
	if(TYPE_SIZE(tp) == 0) 
	    error_at_node(x,"sizeof operator for data of unknown size");
	res1 = expv_int_term(INT_CONSTANT, int_type, TYPE_SIZE(tp));
	return (res1);

    case CAST_EXPR:		/* (CAST type_name expr ) */
	/* type_name = (LIST type_specifier [abstract_declarator]) */
	tp = compile_decl_type_spec(EXPR_ARG1(EXPR_ARG1(x)));
	if ( EXPR_ARG2(EXPR_ARG1(x)) )	/* combine abstract declarator */
	    tp = compile_declarator_type(EXPR_ARG2(EXPR_ARG1(x)), tp);
	v = compile_expression(EXPR_ARG2(x));
	if ( v == NULL ) goto err;

	tq = EXPV_TYPE(v);
	if ( IS_POINTER(tp) && IS_FUNCTION(tq)) {
	    res0 = expv_retype(pointer_type(tp), v);
	    res1 = expv_cons(CAST_EXPR, tp, res0, NULL);
	    return res1;
	}
	if ( IS_POINTER(tp) && IS_POINTER(tq) ){
	    res1 = expv_cons(CAST_EXPR, tp, v, NULL);
	    return (res1);
	}
	else if ( IS_ARITHMETIC(tp) && IS_ARITHMETIC(tq) ){
	    res1 = expv_type_conversion(tp, v);
	    return (res1);
	}
	else if ( IS_POINTER(tp) && IS_INTEGRAL(tq) ){
	    if ( TYPE_SIZE(tp) != TYPE_SIZE(tq) ){
		warning_at_node(x, "cast to pointer from integer of different size");
		res0 = expv_cons(CAST_EXPR, unsigned_int_type, v, NULL);
		res1 = expv_cons(CAST_EXPR, tp, res0, NULL);
		return (res1);
	    }
	    res1 = expv_type_conversion(tp, v);
	    return (res1);
	}
	else if ( IS_INTEGRAL(tp) && IS_POINTER(tq) ){
	    if ( TYPE_SIZE(tp) != TYPE_SIZE(tq) ){
		warning_at_node(x, "cast from pointer to integer of different size");
		res0 = expv_cons(CAST_EXPR, unsigned_int_type, v, NULL);
		res1 = expv_cons(CAST_EXPR, tp, res0, NULL);
		return (res1);
	    }
	    res1 = expv_type_conversion(tp, v);
	    return (res1);
	} else if(IS_VOID(tp)){
	    /* any type can be casted by void. */
	    return expv_cons(CAST_EXPR,tp,v,NULL);
	}

	if ( IS_POINTER(tp) )
	    error_at_node(x, "cannot convert to a pointer type");
	else
	    error_at_node(x, "illegal cast operation");
	goto err;

    case CONDITIONAL_EXPR:
		/* (CONDITIONAL_EXPR condition true-expr false-expr) */
	v = compile_logical_expression(EXPR_ARG1(x));
	left = compile_expression(EXPR_ARG2(x));
	if (IS_FUNCTION(EXPV_TYPE(left))) {
	    left = expv_retype(pointer_type(EXPV_TYPE(left)), left);
	}
	right = compile_expression(EXPR_ARG3(x));
	if (IS_FUNCTION(EXPV_TYPE(right))) {
	    right = expv_retype(pointer_type(EXPV_TYPE(right)), right);
	}
	if ( v == NULL || left == NULL || right == NULL )
	    goto err;
	if ( !EXPV_IS_SCALAR(v) ){
	    error_at_node(x, "invalid logical operand to ?");
	    goto err;
	}
	if ( EXPV_IS_ARITHMETIC(left) && EXPV_IS_ARITHMETIC(right) ){
	    tp = arithmetic_operator_type(left, right);
	    if ( tp == NULL )
		goto invalid_operand;
	}
	else if ( EXPV_IS_POINTER(left) && EXPV_IS_ZERO(right) )
	    tp = EXPV_TYPE(left);
	else if ( EXPV_IS_POINTER(right) && EXPV_IS_ZERO(left) )
	    tp = EXPV_TYPE(right);
	else if ( is_compatible_type(EXPV_TYPE(left), EXPV_TYPE(right)) )
	    tp = EXPV_TYPE(left);	/* take left */
	else
	    error_at_node(x, "operand : has incompatible types");
	res2 = expv_type_conversion(tp, right);
	res0 = expv_cons(LIST, NULL, expv_type_conversion(tp, left), res2);
	res1 = expv_cons(CONDITIONAL_EXPR, tp, v, res0);
	return (res1);

    default:
	break;
    }

    fatal("compile_expression: unknown opcode %d", EXPR_CODE(x));

invalid_operand:
    error_at_node(x, "invalid operand type to %s",
		  (EXPR_CODE_SYMBOL(EXPR_CODE(x)) != NULL) ?
		EXPR_CODE_SYMBOL(EXPR_CODE(x)) : EXPR_CODE_NAME(EXPR_CODE(x)));
err:
    return (NULL);

binary_op:
    if (asg_op){
	res2 = expv_type_conversion(EXPV_TYPE(left), right);
	res1 = expv_cons(EXPR_CODE(x), EXPV_TYPE(left), left, res2);
	return (res1);
    }
    res2 = expv_type_conversion(tp, right);
    res0 = expv_type_conversion(tp, left);
    res1 = expv_cons(EXPR_CODE(x), tp, res0, res2);
    return (res1);

log_binary_op:
    res2 = expv_type_conversion(tp, right);
    res0 = expv_type_conversion(tp, left);
    res1 = expv_cons(EXPR_CODE(x), int_type, res0, res2);
    return (res1);

unary_op:
    res0 = expv_type_conversion(tp, v);
    res1 = expv_cons(EXPR_CODE(x), tp, res0, NULL);
    return (res1);
}

/* left-side expression analysis */
expv    compile_lvalue_expression(x)
    expr    x;
{
    expv    v;

    v = compile_expression(x);
    if ( v == NULL )
	return (v);

    if ( EXPV_CODE(v) != POINTER_REF ){
	error_at_node(x, "illegal lvalue of assignment expression");
	return (NULL);
    }

    return (v);
}

/* for logical expression??? */
expv    compile_logical_expression(expr x)
{
    expv    v;

    v = compile_expression(x);
    if ( v == NULL )
	return (NULL);

    if ( !EXPV_IS_SCALAR(v) )	/* check type */
	error_at_node(x, "bad logical expression");

    return (v);
}

/* coerce type of operand */
expv    expv_type_conversion(TYPE_DESC tp, expv v)
{
    TYPE_DESC tq;

    tq = EXPV_TYPE(v);

    if ( tp == NULL )	/* conversion is not needed */
	return (v);
    if ( type_equal(tp, tq) )
	return (v);

    return (expv_cons(CAST_EXPR, tp, v, NULL));
}

/* simply call to compile_expression */
expv    eval_expression(expr    x)
{
    expv    v;

    v = compile_expression(x);
    v = expv_reduce(v);
    return (v);
}

/* binary operation type check */
int     check_assignment_type(TYPE_DESC tp,
			      expv    v,
			      expr    x, /* FOR error message(parser tree) */
			      char   *msg /* FOR error message */)
{
    TYPE_DESC tq;

    if(IS_UNDEF(tp)) return (FALSE);

    tq = EXPV_TYPE(v);
	/* coersion is possible between arithmetic types */
    if ( (is_integral_type(tp) || is_float_type(tp)) &&
		(is_integral_type(tq) || is_float_type(tq)) )
	return (TRUE);

	/* assignment with NULL pointer */
    if ( IS_POINTER(tp) && EXPV_IS_ZERO(v) )
	return (TRUE);

	/* compatible types ok */
    if ( is_compatible_type(tp, tq) )
	return (TRUE);

	/* else error or warning(for pointer or array) */
    if ( IS_POINTER(tp) && IS_POINTER(tq) ){
	warning_at_node(x, "incompatible pointer types in %s", msg);
	return (TRUE);
    }

    if ( (IS_POINTER(tp) && is_integral_type(tq)) ||
		(is_integral_type(tp) && IS_POINTER(tq)) ){
	warning_at_node(x, "illegal conversion to pointer from integer in %s", msg);
	return (TRUE);
    }

    if ( IS_POINTER(tp) && IS_FUNCTION(tq) ){
	return (TRUE);
    }

    if ( IS_FUNCTION(tp) && IS_FUNCTION(tq) ){
        return (TRUE);
    }

    error_at_node(x, "incompatible types in %s", msg);
    return (FALSE);
}

/*
 * type conversions
 */
/*
 * '%', '>>' '<<' (shift), '&','|','^' (bit-wise operation)
 * NULL: type is not integral, (exprssion's) TYPE: integral(except ENUM, now)
 */
TYPE_DESC integral_operator_type(expv left,expv right)
{
    TYPE_DESC tp, left_tp, right_tp;
    int  lsize = -INT_MAX;
    int  rsize = -INT_MAX;

    left_tp = EXPV_TYPE(left);
    if ( !IS_INTEGRAL(left_tp) )
	return (NULL);	/* left is not int type or ENUM */

	/* ENUM size cannot get by 'basic_type_size' */
    if ( TYPE_DESC_CODE(left_tp) == BASIC_TYPE_NODE )
	lsize = basic_type_size(TYPE_BASIC_TYPE(left_tp));
    else if ( TYPE_DESC_CODE(left_tp) == ENUM_TYPE )
	lsize = enum_type_size;

    if ( right == NULL )	/* unary */
	tp = left_tp;
    else {			/* binary */
	right_tp = EXPV_TYPE(right);
	if ( !IS_INTEGRAL(right_tp) )
	    return (NULL); /* right is not char, short, int, long or ENUM */

		/* ENUM size cannot get by 'basic_type_size' */
	if ( TYPE_DESC_CODE(right_tp) == BASIC_TYPE_NODE )
	    rsize = basic_type_size(TYPE_BASIC_TYPE(right_tp));
	else if ( TYPE_DESC_CODE(right_tp) == ENUM_TYPE )
	    rsize = enum_type_size;

	if ( TYPE_DESC_CODE(left_tp) == ENUM_TYPE ){
	    if ( TYPE_DESC_CODE(right_tp) == ENUM_TYPE )
		return  (left_tp);
	}

	/* coerce to the larger type(not applicable to the ENUM!) */
	if ( lsize > rsize )
	    tp = left_tp;
	else{
	    tp = right_tp;
	    lsize = rsize;
	}

	if ( IS_UNSIGNED(left_tp) || IS_UNSIGNED(right_tp) )
	    if ( !IS_UNSIGNED(tp) )
		tp = unsigned_int_type;
    }

    /* compuation result have the larger than int */
    if ( lsize < basic_type_size(TYPE_BASIC_TYPE(int_type)) )
	return (int_type);
    else
	return (tp);

    /* problems on longlong !!! */
}

/* '+','-','*','%':: caution!! enum, '==' are not considered. */
TYPE_DESC arithmetic_operator_type(expv left, expv right)
{
    TYPE_DESC tp, left_tp, right_tp;

    /* first, check if it is integral */
    tp = integral_operator_type(left, right);
    if ( tp != NULL )
	return (tp);

    left_tp = EXPV_TYPE(left);
    if ( !IS_ARITHMETIC(left_tp) )
	return (NULL);

    if ( right == NULL )	/* unary */
	return (left_tp);

    /* binary */
    right_tp = EXPV_TYPE(right);
    if ( !IS_ARITHMETIC(right_tp) )
	return (NULL);

    /* if either is double, then return it */
    if ( IS_FLOAT(left_tp) && !IS_FLOAT(right_tp) )
	return (left_tp);
    if ( !IS_FLOAT(left_tp) && IS_FLOAT(right_tp) )
	return (right_tp);

    if(TYPE_DESC_CODE(left_tp) == ENUM_TYPE) left_tp = int_type;
    if(TYPE_DESC_CODE(right_tp) == ENUM_TYPE) right_tp = int_type;

    /* coerce to the larger type */
    if ( basic_type_size(TYPE_BASIC_TYPE(left_tp)) >
		basic_type_size(TYPE_BASIC_TYPE(right_tp)) )
	return (left_tp);
    else
	return (right_tp);
}

static void check_args_with_prototype(expv params,expv args,
			      expr x /* for error message */)
{
    expv p = NULL;
    expv v;
    list lp,lq;
    char msg[20];
    int i;

    if(params == NULL) return; /* no prototype ? */
    if(args == NULL){ /* must be f(void) */
	if(EXPR_LIST(params) != NULL) error_at_node(x,"too few arguments");
	return;
    }

    lp = EXPR_LIST(params);
    lq = EXPR_LIST(args);
    for(i=0 ; lp != NULL && lq != NULL; i++){
	p = LIST_ITEM(lp);
	v = LIST_ITEM(lq);
	if(p != NULL){ /* match with any args */
	    sprintf(msg,"passing arg %d\n",i+1);
	    if(v != NULL) check_assignment_type(EXPV_TYPE(p),v,x,msg);
	}
	lq = LIST_NEXT(lq);
	if(p != NULL) lp = LIST_NEXT(lp);
    }
    if(lq != NULL){
	error_at_node(x,"too many arguments");
	return;
    }
    if(lp != NULL){
	if(p == NULL || LIST_ITEM(lp) == NULL) return; /* OK */
	error_at_node(x,"too few arguments");
	return;
    }
}
