static char rcsid[] = "$Id: C-compile-decl.c,v 1.2 2002/02/16 20:53:58 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.
 *  
 *  
 *  $
 */
/* misc functions for yacc transformation */

#include "C-front.h"
#include <limits.h>

	/* function prototype */
static expr	declarator_name _ANSI_ARGS_((expr decl));	/* extract declartor name */
static expv	compile_initializer _ANSI_ARGS_((TYPE_DESC tp, expr init, int must_const));
static list	compile_initializer_list _ANSI_ARGS_((TYPE_DESC tp, list lp, expv *vp,int must_const));
static void 	initialize_compile_function _ANSI_ARGS_((void));
static void 	check_prev_function_prototype _ANSI_ARGS_((ID id, TYPE_DESC tp, expr name));
static void 	compile_function_def _ANSI_ARGS_((expr x, STORAGE_CLASS class));
static void      output_decl _ANSI_ARGS_((expv v));

/* define TAGNAME and return type descriptor */
static TYPE_DESC	define_tag_name _ANSI_ARGS_((expr    name, enum expr_code code, int def_flag));
static expv	compile_prototype_param_list _ANSI_ARGS_((expr param_decl_list));
static int have_QUAL_INLINE _ANSI_ARGS_((expr x));

/* Scopes */
/* LEVAL==0: global scope, LEVEL==1: PARAMTER, LEVEL>1: NESTED BLOCK */
#define GLOBAL_LEVEL 0
#define PARAM_LEVEL 1
#define LABEL_LEVEL 2		/* level in where label is defined */

int     current_level = 0;
ID      id_lists[MAX_NEST_LEVEL];	/* global ID list for each level */
expv    decl_lists[MAX_NEST_LEVEL];
int  	param_level;
int 	label_level;

/* "break" and "continue" scope */
ID      switch_labels[MAX_SWITCH_LEVEL];
int     break_level;
int     continue_level;
int     switch_level;

TYPE_DESC return_type;	/* current function return value type */

/* ptr to the predefined table */
TYPE_DESC default_data_type;
TYPE_DESC default_func_type;
TYPE_DESC void_pointer_type, string_type, long_type, int_type, float_type;
TYPE_DESC unsigned_int_type, longlong_type;
TYPE_DESC basic_type_desc[N_BASIC_TYPE];
TYPE_DESC error_type;		/* for error recovery */
expv    expv_constant_0, expv_constant_1, expv_constant_m1;
expv    expv_float_0;

/* global initialization and frequently used tables(basic type, etc) */
void initialize_compile()
{
    int     t;
    TYPE_DESC tp;

    current_level = 0;		/* global scope level reset */
    GLOBAL_DECL_LIST = list0(LIST);	/* initial list node */

	/* predefine basic type descriptor(table) */
    for (t = 0 ; t < N_BASIC_TYPE ; t++) {
	tp = new_type_desc(BASIC_TYPE_NODE);
	TYPE_BASIC_TYPE(tp) = (BASIC_DATA_TYPE)t;
	TYPE_SIZE(tp) = basic_type_size((BASIC_DATA_TYPE)t);
	TYPE_ALIGN(tp) = basic_type_align((BASIC_DATA_TYPE)t);
	basic_type_desc[t] = tp;
    }

	/* another name for convenient use */
    int_type = BASIC_TYPE_DESC(INT);
    unsigned_int_type = BASIC_TYPE_DESC(UNSIGNED);
    long_type = BASIC_TYPE_DESC(LONG);
    longlong_type = BASIC_TYPE_DESC(LONGLONG);
    float_type = BASIC_TYPE_DESC(DOUBLE);	/* default, double float */
    string_type = pointer_type(BASIC_TYPE_DESC(CHAR));
    void_pointer_type = pointer_type(BASIC_TYPE_DESC(VOID));
	/* another name for default type */
    default_data_type = int_type;
    default_func_type = func_type(int_type);
	/* special node */
    error_type = new_type_desc(ERROR_NODE);

	/* frequently used constant table */
    expv_constant_0 = expv_int_term(INT_CONSTANT, int_type, 0);
    expv_constant_1 = expv_int_term(INT_CONSTANT, int_type, 1);
    expv_constant_m1 = expv_int_term(INT_CONSTANT, int_type, -1);
    expv_float_0 = expv_float_term(float_type, 0.0);
}

/* reset global variables for procedure compilation */
static void initialize_compile_function()
{
    /* current_level = 0; */
    param_level = current_level + PARAM_LEVEL;
    label_level = current_level + LABEL_LEVEL;
    break_level = 0;
    continue_level = 0;
}

static void output_decl(expv v)
{
    if (debug_X_flag){
	fprintf(debug_fp, "### --- output_decl: level=%d ---\n",
		current_level);
	X_output(v, debug_fp);	/* for debug out */
	fprintf(debug_fp, "###------------------------------------\n");
    }
    CURRENT_DECL_LIST = list_put_last(CURRENT_DECL_LIST, v);
}

void extern_C_begin()
{
    output_decl(expv_int_term(CXX_ATTR_DECL,NULL,CXX_EXTERN_C_BEGIN));
}

void extern_C_end()
{
    output_decl(expv_int_term(CXX_ATTR_DECL,NULL,CXX_EXTERN_C_END));
}

void    compile_function(expr x)
{
    if (debug_expr_flag) {
	fprintf(debug_fp, "### --- compile_function input data ---\n");
	expr_print(x, debug_fp);
	fprintf(debug_fp, "###------------------------------------\n");
	return;
    }

    compile_function_def(x,EXTDEF);
}

/*
 * external declaration :=
 * (LIST decl_specifier (LIST (LIST declarator initializer) ...))
 * (TYPE_DEFINITION type_specifier (LIST (LIST declarator initializer) ...))
 */
void compile_external_declaration(expr x)
{
    expv    v;
    
    if(x == NULL) return; /* error recovery */

    if (debug_expr_flag) {	/* parser data out */
	fprintf(debug_fp, "#--- compile_external_declaration input ---\n");
	expr_print(x, debug_fp);
	fprintf(debug_fp, "#------------------------------------------\n");
	return;
    }

    if(EXPR_CODE(x) == PRAGMA_LINE){
	v = compile_pragma_line(x);
	output_decl(v);
	return;
    }

    compile_declaration(x, EXTDEF);
}

/*
 * function declaration :=
 * '(FUNCTION_DEFINTIION decl_spec declarator args compound_statement)'
 */
static void compile_function_def(expr x, STORAGE_CLASS class)
{
    TYPE_DESC type;
    expr    decl_spec, decl, name, param_decl_list, param_list;
    list    lp;
    ID      id, func_id, param_ids;
    ID      idp = NULL;
    expv    w, decls, v, vv1, vv2;

    if(x == NULL) return;

    if(EXPR_ARG1(x) != NULL /* decl_spec not NULL */
       && have_QUAL_INLINE(EXPR_ARG2(EXPR_ARG1(x)))){
	if(EXPR_CODE(EXPR_ARG2(x)) == FUNCTION_PROTO_DECL){
	    /* __inline__ is not supported. just convert protype decl */
	    compile_declaration(list2(LIST,EXPR_ARG1(x),
				      list1(LIST,EXPR_ARG2(x))),
				class);
	}
	return;
    }

    initialize_compile_function();	/* reset per procedure vars */

	/* 1.function (return) type part */
    decl_spec = EXPR_ARG1(x);

    if (decl_spec == NULL)
	type = compile_decl_type_spec(NULL);	/* gen ID */
    else {
	type = compile_decl_type_spec(EXPR_ARG2(decl_spec));
	if (EXPR_ARG1(decl_spec)) {	/* storage class */
	    class = EXPR_SCLASS(EXPR_ARG1(decl_spec));	/* overwrite class */
	    switch (class) {
	    case STATIC:
		break;

	    case REGISTER:
	    case AUTO:	/* illegal type: check # err and exit/fall throgh */
		error_at_node(decl_spec,
			      "bad storage class for function definition");
	    case EXTERN:
	    default:
		class = EXTDEF;	/* overwrite again */
	    }
	}
    }

	/* 2. func name (and pointer) */
    decl = EXPR_ARG2(x);
    type = compile_declarator_type(decl, type);	/* table update for '*' */
    name = declarator_name(decl);

    if (debug_X_flag) {
	fprintf(debug_fp, "------------------------------------\n");
	fprintf(debug_fp, "Function [%s] return type [\n", name->v.e_sym->s_name);
	print_type(type, debug_fp);
	fprintf(debug_fp, "\n]\n");
	fprintf(debug_fp, "------------------------------------\n");
    }

    if (TYPE_DESC_CODE(type) != FUNCTION_DECL)
	fatal("compile_function");

	/* gen and link func name's ID list */
    return_type = TYPE_REF(type);
    func_id = define_ident(name, class, type);	/* gen ID */

    PUSH_ENV;	/* change scope (to internal func) */

	/* 3. argument decl */
    if (TYPE_IS_FUNC_PROTO(type)){	/* ANSI-style type decl */
	decls = list0(LIST);
	FOR_ITEMS_IN_LIST(lp,TYPE_FUNC_PARAMS(type)){
	    v = LIST_ITEM(lp);
	    if(v == NULL) continue;
	    if(EXPR_CODE(v) != IDENT){
		error_at_node(x,"parameter name is missing");
		continue;
	    }
	    id = define_ident(v,PARAM,EXPV_TYPE(v));
	    /* convert array type parameter to pointer type */
	    if(IS_ARRAY(ID_TYPE(id)) && !array_arg_flag)
		ID_TYPE(id) = pointer_type(TYPE_REF(ID_TYPE(id)));
	    ID_BASE(id) = 
		expv_sym_term(PARAM_ADDR, pointer_type(ID_TYPE(id)), 
			      ID_SYM(id));
	    decls = list_put_last(decls,list2(VAR_DECL, v, NULL));
	}
    } else {	/* K&R style parameter declaration */
	param_list = TYPE_FUNC_PARAMS(type);	/* name list in bracket */
	param_decl_list = EXPR_ARG3(x);		/* type decl (K&R style) */

	/* parameter name define as SNULL, convert to PARM after */
	FOR_ITEMS_IN_LIST(lp, param_decl_list){
	    compile_declaration(LIST_ITEM(lp), SNULL);
	}
	decls = CURRENT_DECL_LIST;

	/* reorder parameter list */
	param_ids = CURRENT_ID_LIST;
	CURRENT_ID_LIST = NULL;
	if (param_list == NULL){	/* no parameter in the bracket */
	    if (param_decl_list != NULL){
		error_at_node(param_decl_list, 
			      "illegal parameter declaration");
	    }
	} else {
	    FOR_ITEMS_IN_LIST(lp, param_list){
		/* allocate paramters in order of param_list */
		name = LIST_ITEM(lp);
		for(id = param_ids; id != NULL; idp = id, id = ID_NEXT(id))
		    if ( ID_CLASS(id) != TAGNAME && 
			 ID_SYM(id) == EXPR_SYM(name))
			break;

		if(id != NULL){	/* remove from prevous list */
		    if(id == param_ids) param_ids = ID_NEXT(id);
		    else ID_NEXT(idp) = ID_NEXT(id);
		    ID_NEXT(id) = NULL;
		    /* put it at the tail of ID_LIST */
		    if(CURRENT_ID_LIST == NULL) CURRENT_ID_LIST = id;
		    else {
			for(idp = CURRENT_ID_LIST; ID_NEXT(idp) != NULL; 
			    idp = ID_NEXT(idp)) ;
			ID_NEXT(idp) = id;
		    }
		} else {		/* gen ID */
		    id = define_ident(name, SNULL, default_data_type);
		}

		if (ID_CLASS(id) != SNULL && ID_CLASS(id) != REGISTER) {
		    error_at_node(name, 
				  "redeclaration of formal parameter, '%s'",
				  ID_NAME(id));
		    continue;
		}
		
		/* allocate parameter */
		ID_CLASS(id) = PARAM;
		if(IS_ARRAY(ID_TYPE(id))) 
		    ID_TYPE(id) = pointer_type(TYPE_REF(ID_TYPE(id)));
		ID_BASE(id) = 
		    expv_sym_term(PARAM_ADDR, pointer_type(ID_TYPE(id)), 
				  ID_SYM(id));
	    }
	}

	/* check current enviroment for variables missing declaration */
	for (id = param_ids ; id != NULL ; id = ID_NEXT(id)){
	    if (ID_CLASS(id) == SNULL){
		if (param_list != NULL){
		    error_at_node(param_list,
				  "declared argument \"%s\" is missing", 
				  ID_NAME(id));
		} else {	/* search input expr for line# */
		    error_at_node( param_decl_list,
				   "declared argument \"%s\" is missing", 
				   ID_NAME(id));
		} 
	    } else {  /* may be tagname ?? */
		fatal("unknown storage class on param list");
	    }
	}
    }

	/* 4 compile procedure body */
    w = compile_compound_statement(EXPR_ARG4(x));

	/* make list for whole procedure
	 * 'proc_name->ID_list->declarations->body' */
    vv1 = expv_sym_term(IDENT, NULL, ID_SYM(func_id));
    vv2 = expv_any_term(ID_LIST, (void *)CURRENT_ID_LIST);
    w = list4(FUNCTION_DEFINITION, vv1, vv2, decls, w);

    POP_ENV;	/* level out */

    output_decl(w);
}

/*
 * compile declarations, both external and internal, with default class
 * "decl_specifier = (LIST class type)"
 */
void   compile_declaration(expr    x,	/* parser's declaration structure */
			   STORAGE_CLASS class /* default stoarge class */)
{
    TYPE_DESC type, type2;
    expr    decl_spec, l, decl, name, init;
    list    lp;
    ID      id;
    expv    v1 = NULL;
    expv    v2;
    STORAGE_CLASS class2;	/* stoarge class */

    if (x == NULL) return;	/* nothing */

    if (EXPR_CODE(x) != LIST) {	/* typedef declarations */
	if(EXPR_CODE(x) == PRAGMA_LINE){
	    error_at_node(x,"pragma is not allowed here");
	    return;
	}
	if (EXPR_CODE(x) != TYPEDEF_DECL)
	    fatal("bad declaration code");
	compile_type_definition(EXPR_ARG1(x), EXPR_ARG2(x));
	return;
    }

	/* decl_spec = (LIST class type) */
    decl_spec = EXPR_ARG1(x);	/* type part in parser data */
    type = compile_decl_type_spec(EXPR_ARG2(decl_spec));/* gen ID */

	/* move ID to the last */
    l = EXPR_ARG2(decl_spec);
    if ( l != NULL )
	if ( EXPR_CODE(l) == STRUCT_TYPE || EXPR_CODE(l) == UNION_TYPE ||
	     EXPR_CODE(l) == ENUM_TYPE ){
	    if ( EXPR_ARG2(l) != NULL ){
		move_id_last(type, l);
	    }
	}
    l = NULL;

    if (debug_X_flag) {
	fprintf(debug_fp, "Declared type [\n");
	print_type(type, debug_fp);
	fprintf(debug_fp, "\n]\n");
    }

	/* if class is specified, overwrite(enum) */
    if (EXPR_ARG1(decl_spec) != NULL)
	class = EXPR_SCLASS(EXPR_ARG1(decl_spec));

	/* identifier(s) */
    l = EXPR_ARG2(x);
    if ( l == NULL ) return;	/* no identifier */

    FOR_ITEMS_IN_LIST(lp, l) {	/* comma separated sequence process */
		/* decl := 'declarator' | '(LIST declarator init)' */
	decl = LIST_ITEM(lp);
	if (decl == NULL)
	    continue;		/* error recovery */
	if (EXPR_CODE(decl) == LIST) {
	    init = EXPR_ARG2(decl);
	    decl = EXPR_ARG1(decl);
	}
	else
	    init = NULL;

	type2 = compile_declarator_type(decl, type);
	type2 = combine_type_qual(EXPR_ARG2(decl_spec),type2);

	name = declarator_name(decl);	/* symbol name */

	if ( debug_X_flag && type != type2 ){
	    fprintf(debug_fp, "Declared type update for '%s'[\n", name->v.e_sym->s_name);
	    print_type(type2, debug_fp);
	    fprintf(debug_fp, "\n]\n");
	}

	/* if function declaration is in local decl, then it must be extern */
	if ( IS_FUNCTION(type2) ){
	    if ( class != STATIC ) class2 = EXTERN;
	    else class2 = STATIC;
	    id = define_ident(name, class2, type2);	/* gen ID */

	    if (debug_X_flag){
		fprintf(debug_fp, "ID table for function decl------\n");
		print_ID(id, debug_fp);
		fprintf(debug_fp, "--------------------------------\n");
	    }

	    if (init)
		error_at_node(init, "function is initialized like a variable");

	    if (!TYPE_IS_FUNC_PROTO(type2) && TYPE_FUNC_PARAMS(type2) != NULL)
		warning_at_node(x, "parameter names in function declaration");

	    v2 = expv_sym_term(IDENT, NULL, ID_SYM(id));
	    v1 = list2(EXT_DECL, v2, NULL);

	    output_decl(v1);

	    continue;	/* function decl end */
	} else {	/* check type size */
	    if(class != EXTERN && class != SNULL && init == NULL &&
	       TYPE_SIZE(type2) == 0){
		error_at_node(x,"storage size of '%s' is unknown",
			      SYM_NAME(EXPR_SYM(name)));
		TYPE_DESC_CODE(type2) = BASIC_TYPE_NODE;
		TYPE_BASIC_TYPE(type2) = UNDEF;
	    }
	}

	/*
	 * initailzer must be evaluated before definition to give size for type
	 */
	if (init) {
	    if(((IS_STRUCT(type2) || IS_UNION(type2)) && TYPE_MEMBER_LIST(type2) == NULL) ||
	       ((IS_ENUM(type2)) && TYPE_MOE_LIST(type2) == NULL)){
		error_at_node(name,"initialize incomplete type variable '%s'",
			      SYM_NAME(EXPR_SYM(name)));
		v1 = NULL;
	    } else 
		v1 = compile_initializer(type2, init,
					 (class != AUTO && class != REGISTER));
	}

	id = define_ident(name, class, type2);	/* gen ID */

	if (debug_X_flag){
	    fprintf(debug_fp, "ID table for %s--------\n",
			name->v.e_sym->s_name);
	    print_ID(id, debug_fp);
	    fprintf(debug_fp, "--------------------------------\n");
	}

	switch (class) {
	case EXTERN:	/* fall through */
	    if (init) {
		error_at_node(init, "extern '%s' is initialized", ID_NAME(id));
	    }
	    break;
	case EXTDEF:
	case STATIC:
	case AUTO:
	case REGISTER:
	    if(((IS_STRUCT(type2) || IS_UNION(type2)) 
		&& TYPE_MEMBER_LIST(type2) == NULL) ||
	       ((IS_ENUM(type2)) && TYPE_MOE_LIST(type2) == NULL)){
		if(!ID_ERROR(id)){
		    error_at_node(name,"stroage size of '%s' is unknown",
				  ID_NAME(id));
		    ID_ERROR(id) = TRUE;
		}
	    }
	    if(IS_ARRAY(type2) && TYPE_ARRAY_DIM(type2) == 0){
		error_at_node(name,"array '%s' has no size",ID_NAME(id));
	    }
	    break;

	case SNULL:
	    if (init)
		fatal("initializer to unknown class");
	    /* for parameter, a[] -> *a ==> a[][] -> *a[][] */
	    if (TYPE_DESC_CODE(ID_TYPE(id)) == ARRAY_DECL){
	        ID_TYPE(id) = pointer_type(TYPE_REF(ID_TYPE(id)));
	    }
	    break;
	default: 
	    break;
	}

	if (init){
	    v2 = expv_sym_term(IDENT, NULL, ID_SYM(id));
	    v1 = list2(VAR_DECL, v2, v1);
	}
	else if ( class == EXTERN ){
	    v2 = expv_sym_term(IDENT, NULL, ID_SYM(id));
	    v1 = list2(EXT_DECL, v2, NULL);
	}
	else{
	    v2 = expv_sym_term(IDENT, NULL, ID_SYM(id));
	    v1 = list2(VAR_DECL, v2, NULL);
	}

	EXPR_LINE(v1) = EXPR_LINE(decl);
	output_decl(v1);
    }
}

/* compile initialize expression */
static expv    compile_initializer(TYPE_DESC tp, expr init, int must_const)
{
    expv    v;
    int     len;

    switch (TYPE_DESC_CODE(tp)) {
    case BASIC_TYPE_NODE:
    case ENUM_TYPE:
    case POINTER_DECL:	/* scalar data type */
	if (EXPR_CODE(init) == LIST) {
	    warning_at_node(init, "braces around scalar initializer");
	    if (LIST_NEXT(EXPR_LIST(init)) != NULL)
		warning_at_node(init, "excess elements in scalar initializer");
	    init = LIST_ITEM(EXPR_LIST(init));
	}
	if(EXPR_CODE(init) == LIST) goto bad_initializer;
	v = compile_init_assignment(tp, init, must_const);
	break;

    case ARRAY_DECL:	/* initalize structure/array element */
	if(IS_BASIC_TYPE(TYPE_REF(tp)) &&
	   (TYPE_BASIC_TYPE(TYPE_REF(tp)) == CHAR ||
	    TYPE_BASIC_TYPE(TYPE_REF(tp)) == UNSIGNED_CHAR)){
	    /* check string initializer */
	    if (EXPR_CODE(init) != LIST) {
		v = compile_expression(init);
		if(v == NULL)  break;	/* error */
		v = expv_reduce(v);
		if(EXPV_CODE(v) == STRING_CONSTANT)
		    goto str_initializer;
		else goto bad_initializer;
	    } else {
		if(EXPR_CODE(LIST_ITEM(EXPR_LIST(init))) != LIST){
		    v = compile_expression(LIST_ITEM(EXPR_LIST(init)));
		    if(v == NULL) break; /* error detected */
		    v = expv_reduce(v);
		    if(EXPV_CODE(v) == STRING_CONSTANT){
			if (LIST_NEXT(EXPR_LIST(init)) != NULL)
			    warning_at_node(init, "excess elements in string initializer");
			goto str_initializer;
		    }
		    /* else normal initializer ? */
		}
	    }
	}
	
    case UNION_TYPE:
    case STRUCT_TYPE:
	if (EXPR_CODE(init) != LIST){
	    if(TYPE_DESC_CODE(tp) == ARRAY_DECL){
		v = compile_expression(init);
		goto bad_initializer;
	    }
	    v = compile_init_assignment(tp, init, must_const);
	    break;
	} 
	v = list0(LIST);
	if(compile_initializer_list(tp, EXPR_LIST(init), &v, must_const)
	   != NULL)
	    error_at_node(init, "too many initializer");
	break;

    str_initializer:
	len = strlen(EXPR_STR(v)) + 1;
	if (len > TYPE_SIZE(tp)){
	    if (TYPE_SIZE(tp) == 0){
		TYPE_ARRAY_DIM(tp) = len;
		TYPE_SIZE(tp) = len;
	    } else
		error_at_node(init,
			      "initializer-string for array of chars is too long");
	} /* else OK */
	break;

    bad_initializer:
	error_at_node(init, "invalid initializer");
	v = NULL;
	break;

    default:
	fatal("compile_initializer: bad type");
    }

    return v;
}

/* initalizer in flat list.  { ...  }. return the rest of init list. */
static list  compile_initializer_list(TYPE_DESC tp, list   lp, expv   *vp,
				int     must_const)
{
    int     i, d;
    TYPE_DESC tp_elem;
    expr    x;
    ID      member;
    expv    v;

    switch (TYPE_DESC_CODE(tp)) {
    default:	/* as default, pick element and initialize scalar */
	v = compile_initializer(tp, LIST_ITEM(lp), must_const);
	*vp = list_put_last(*vp, v);
	return (LIST_NEXT(lp));

    case UNION_TYPE:
      /* break; *//* initialize as same as STRUCT_TYPE */

    case STRUCT_TYPE:
	for (member = TYPE_MEMBER_LIST(tp); member != NULL;
	     member = ID_NEXT(member)) {
	    x = LIST_ITEM(lp);
	    tp_elem = ID_TYPE(member);
	    if (EXPR_CODE(x) == LIST) {
		v = compile_initializer(tp_elem, x, must_const);
		*vp = list_put_last(*vp, v);
		lp = LIST_NEXT(lp);
	    }
	    else
		lp = compile_initializer_list(tp_elem, lp, vp, must_const);
	    if (lp == NULL)
		break;
	}
	break;

    case ARRAY_DECL:
	d = TYPE_ARRAY_DIM(tp);
	tp_elem = TYPE_REF(tp);	/* element type */
	if ( d != 0 ){
	    for ( i = 0 ; i < d ; i++ ){
		x = LIST_ITEM(lp);
		if (EXPR_CODE(x) == LIST) {
		    v = compile_initializer(tp_elem, x, must_const);
		    *vp = list_put_last(*vp, v);
		    lp = LIST_NEXT(lp);
		} else if (EXPR_CODE(x) == STRING_CONSTANT &&
			   TYPE_DESC_CODE(tp_elem) == BASIC_TYPE_NODE){
		    v = compile_initializer(tp, x, must_const);
		    *vp = list_put_last(*vp, v);
		    lp = LIST_NEXT(lp);
		    break;
		} else {
		    lp = compile_initializer_list(tp_elem, lp, vp, must_const);
		}
		if (lp == NULL)
		    break;
	    }
	}
	else {			/* null dimension */
	    do {
		x = LIST_ITEM(lp);
		if (EXPR_CODE(x) == LIST) {
		    v = compile_initializer(tp_elem, x, must_const);
		    *vp = list_put_last(*vp, v);
		    lp = LIST_NEXT(lp);
		}
		else
		    lp = compile_initializer_list(tp_elem, lp, vp, must_const);
		d++;
	    } while (lp != NULL);

	    TYPE_ARRAY_DIM(tp) = d;
	    TYPE_SIZE(tp) = d * TYPE_SIZE(tp_elem);;
	}
    }

    return (lp);
}

/* compile "typedef" declaration */
void compile_type_definition(expr type_spec, expr decl_list)
{
    TYPE_DESC type, type2;
    expr    decl, init, name;
    list    lp;
    ID      id;

    type = compile_decl_type_spec(type_spec);	/* gen ID */

    FOR_ITEMS_IN_LIST(lp, decl_list) {
	decl = LIST_ITEM(lp);	/* decl='declarator'|'(LIST declarator init)'*/
	if ( decl == NULL )
	    continue;		/* error recovery */

	if ( EXPR_CODE(decl) == LIST ){
	    init = EXPR_ARG2(decl);
	    decl = EXPR_ARG1(decl);
	}
	else
	    init = NULL;

	type2 = compile_declarator_type(decl, type);

	name = declarator_name(decl);	/* extract name */
	if ( name == NULL )
	    continue;		/* error recovery */

	id = define_ident(name, TYPEDEF_NAME, type2);	/* gen ID */

	if (debug_X_flag){
	    fprintf(debug_fp, "ID table for ");
	    if ( name->v.e_sym != NULL ){
		if ( name->v.e_sym->s_name != NULL )
		    fprintf(debug_fp, "%s--------\n", name->v.e_sym->s_name);
		else
		    fprintf(debug_fp, "------------\n");
	  } else
	      fprintf(debug_fp, "------------\n");
	    print_ID(id, debug_fp);
	    fprintf(debug_fp, "--------------------------------\n");
	}

	if (init)
	    error_at_node(init, "typedef '%s' has an initializer", ID_NAME(id));
    }
}

/* return type structure */
TYPE_DESC compile_decl_type_spec(expr x /* parser tree */)
{
    ID      id;
    TYPE_DESC tp = NULL;
    TYPE_DESC tp1;
    expr    x1, x2;

    if ( x == NULL ){	/* NULL: default type */
	tp = new_type_desc(BASIC_TYPE_NODE);
	TYPE_BASIC_TYPE(tp) = DEFAULT_TYPE;
	TYPE_SIZE(tp) = basic_type_size(DEFAULT_TYPE);
	TYPE_ALIGN(tp) = basic_type_align(DEFAULT_TYPE);
	return (tp);
    }

    switch (EXPR_CODE(x)) {
    case BASIC_TYPE_NODE:
	tp = basic_type_desc[(int) EXPR_TYPE(x)];
	break;

    case LIST:		/* combined type: recursive call */
	if ( LIST_NEXT(EXPR_LIST(x)) != NULL ){
	    tp1 = compile_decl_type_spec(EXPR_ARG2(x));
	    /* qualify type by cv. ex. const char *p */
	    if(EXPR_CODE(EXPR_ARG1(x)) == TYPE_QUAL_NODE)
		tp = combine_type_qual(EXPR_ARG1(x),tp1);
	    else 
		tp = combine_basic_type(EXPR_ARG1(x), tp1); 
	} else {  	/* maybe default, abstract datatype */
	    tp1 = new_type_desc(BASIC_TYPE_NODE);
	    TYPE_BASIC_TYPE(tp1) = DEFAULT_TYPE;
	    TYPE_SIZE(tp1) = basic_type_size(DEFAULT_TYPE);
	    TYPE_ALIGN(tp1) = basic_type_align(DEFAULT_TYPE);
	    tp = combine_basic_type(EXPR_ARG1(x), tp1);
	}
	break;

    case ENUM_TYPE:	/* (ENUM_TYPE name (LIST enumerator-list ...) */
	x1 = EXPR_ARG1(x);	/* name */
	x2 = EXPR_ARG2(x);	/* (LIST enum ...) */
	if (x2) {
	    tp = define_tag_name(x1, ENUM_TYPE, TRUE);
	    compile_enum_elements(tp, x2);
	}
	else{
	    tp = define_tag_name(x1, ENUM_TYPE, FALSE);
	}
	break;

	/* (STRUCT_TYPE name (LIST declaration ) base_opt) */
    case STRUCT_TYPE:
    case UNION_TYPE:	
    case CLASS_TYPE:
	x1 = EXPR_ARG1(x);	/* tag name */
	x2 = EXPR_ARG2(x);	/* (LIST decl ...) */
	if (x2) {
	    tp = define_tag_name(x1, EXPR_CODE(x), TRUE);
		/* process member list on env 'tp' */
	    compile_struct_declaration(tp, x2);
		/* move struct/union id to the last */
	    if ( tp->p.tagname->ip_next != NULL ){
		ID idt;
		if ( id_lists[current_level] == tp->p.tagname ){
		    id_lists[current_level] = tp->p.tagname->ip_next;
		} else {
		    for( idt = id_lists[current_level] ; idt != NULL ; 
			 idt = ID_NEXT(idt) ){
			if ( idt->ip_next == tp->p.tagname )
			    break;
		    }
		    if ( idt != NULL ){
			idt->ip_next = tp->p.tagname->ip_next;
		    }
		}

		tp->p.tagname->ip_next = NULL;

		for( idt = id_lists[current_level] ; ; idt = ID_NEXT(idt) )
		    if ( idt->ip_next == NULL )
			break;
		idt->ip_next = tp->p.tagname;

	    }
	} else {
	    tp = define_tag_name(x1, EXPR_CODE(x), FALSE);
	}
	define_ident(x1,TYPEDEF_NAME,tp);
	break;

    case TYPENAME_IDENT:
	id = lookup_ident(x);
	if (ID_CLASS(id) != TYPEDEF_NAME)
	    error_at_node(x, "illegal type name");
	tp = ID_TYPE(id);
	break;

    default:
	fatal("compile_decl_type_spec");
	break;
    }

    return (tp);
}

/* compile enumerate */
void compile_enum_elements(TYPE_DESC tp, expr elem_list)
{
    list    lp;
    ID      id,id0;
    expr    x, l;
    expv    v, w;
    int moe_val;

    l = list0(LIST);
    moe_val = 0;
    FOR_ITEMS_IN_LIST(lp, elem_list) {
	x = LIST_ITEM(lp);	/* (name value) */
	id = define_ident(EXPR_ARG1(x), MOE, tp);	/* gen ID */
	if (EXPR_ARG2(x)) {
	    v = eval_expression(EXPR_ARG2(x));
	    switch(EXPV_CODE(v)){
	    case INT_CONSTANT:
		ID_BASE(id) = v;
		moe_val = EXPV_INT_VALUE(v)+1;
		break;
	    case LONG_CONSTANT: {
		/*
		 * enum is int. long must be shrinked to int.
		 */
		long longMoe = EXPV_LINT_VALUE(v) + (long)1;
		ID_BASE(id) = v;
		moe_val = (int)longMoe;
		break;
	    }
	    case MOE_CONSTANT:
		id0 = lookup_ident(v);
		if(id0 != NULL && ID_CLASS(id0) == MOE){
		    ID_BASE(id) = ID_BASE(id0);
		    moe_val = EXPV_INT_VALUE(ID_BASE(id0))+1;
		    break;
		}
		/* fall through */
	    default:
		ID_BASE(id) = expv_constant_1; /* error */
		error_at_node(EXPR_ARG2(x),
			      "enumerator value is not integer constant");
	    }
	} else {
	    ID_BASE(id) = expv_int_term(INT_CONSTANT,int_type,moe_val++);
	}
	w = expv_sym_term(IDENT, NULL, ID_SYM(id));
	l = list_put_last(l, w);
    }

    TYPE_MOE_LIST(tp) = l;
    TYPE_SIZE(tp) = enum_type_size;
    TYPE_ALIGN(tp) = enum_type_align;
}

/* compile struct/union: if (TYPE_MEMBER_LIST(tp)!=NULL), may be useless */
void compile_struct_declaration(
    TYPE_DESC tp,	/* transformed type */
    expr    decl_list	/* parser list */)
{
    int     offset, align, len, bits;
    list    lp;
    expr    x;
    expv    v;
    ID      id, member_list;

    PUSH_ENV;

    FOR_ITEMS_IN_LIST(lp, decl_list) {
	x = LIST_ITEM(lp);	/* '(LIST type_spec declarator_list)' */
	if (x == NULL)	    continue;		/* error recovery */

	switch(EXPR_CODE(x)){
	case FUNCTION_DEFINITION:
	    compile_function_def(x,MEMBER);
	    break;
	case CXX_ATTR_DECL:
	    output_decl(x);
	    break;
	default:
	    compile_declaration(x, MEMBER);
	}
    }

    /*
    expr_print_msg("at TYPE_DEF",CURRENT_DECL_LIST,debug_fp);
    print_id_list("at TYPE_DEF", CURRENT_ID_LIST, debug_fp);
    */

    v = expv_cons(TYPE_DEFINITION,tp,
		  expv_any_term(ID_LIST, (void *)CURRENT_ID_LIST),
		  CURRENT_DECL_LIST);
    member_list = CURRENT_ID_LIST;

    POP_ENV;
    
    output_decl(v);

    /* counting total size, for type checking */
    offset = 0;
    align  = 0;
    bits   = 0;
    if ( IS_STRUCT(tp) ){
	for ( id = member_list ; id != NULL ; id = ID_NEXT(id) ){
	    if (ID_BIT_FIELD_LEN(id)) {
		len = ID_BIT_FIELD_LEN(id);
		if (len + bits > (TYPE_SIZE(ID_TYPE(id)) * CHAR_BIT)) {
		    if (bits) {
			align_offset(int_type, &offset);
			offset += TYPE_SIZE(int_type);
			align   = MAX(align, TYPE_ALIGN(int_type));
		    }
		    bits = len;
		} else {
		    bits += len;
		}
	    } else {
		if (bits) {
		    align_offset(int_type, &offset);
		    offset += TYPE_SIZE(int_type);
		    align   = MAX(align, TYPE_ALIGN(int_type));
		    bits    = 0;
		}

		align_offset(ID_TYPE(id), &offset);
		offset += TYPE_SIZE(ID_TYPE(id));	/* size in byte */
		align   = MAX(align, TYPE_ALIGN(ID_TYPE(id)));
	    }
	}
	if (bits) {
	    align_offset(int_type, &offset);
	    offset += TYPE_SIZE(int_type);
	    align   = MAX(align, TYPE_ALIGN(int_type));
	}
    }
    else {	/* union */
	for ( id = member_list ; id != NULL ; id = ID_NEXT(id) ){
	    offset = MAX(offset, TYPE_SIZE(ID_TYPE(id)));
	    align = MAX(align, TYPE_ALIGN(ID_TYPE(id)));
	}
    }
	/* alignment */
    if ( align != 0 )
	offset = ROUND(offset, align);

	/* link member ID list */
    if ( TYPE_MEMBER_LIST(tp) == NULL ){
	TYPE_MEMBER_LIST(tp) = member_list;
	TYPE_SIZE(tp) = offset;
	TYPE_ALIGN(tp) = align;
    }
}

/* declarator := 'IDENT' | '(FUNCTION_DECL declarator)' |
 * '(ARRAY_DECL declarator size)' | '(POINTER_DECL declarator)' */
/* combine decl specifier and declarator */
TYPE_DESC compile_declarator_type( expr    decl,  TYPE_DESC tp)
{
    expr	dim;
    TYPE_DESC	tq;
    expv	v;
    list	lp;
    ID		id;


    if (decl == NULL)	/* for abstract declarator */
	return (tp);

    switch (EXPR_CODE(decl)) {
    case IDENT:
	return (tp);

    case POINTER_DECL:
	tq = pointer_type(tp);	/* alloc ptr type, ref to type(tp) */
	if(EXPR_ARG1(decl) != NULL && EXPR_CODE(EXPR_ARG1(decl)) == LIST){
	    /* for type qualifier, or cv-qualifier list */
	    /* qualify the pointer itself. ex. char * const x; */
	    FOR_ITEMS_IN_LIST(lp, EXPR_ARG2(EXPR_ARG1(decl)))
		tq = combine_type_qual(LIST_ITEM(lp), tq);
	    tp = compile_declarator_type(EXPR_ARG1(EXPR_ARG1(decl)), tq);
	} else 
	    tp = compile_declarator_type(EXPR_ARG1(decl), tq);
	return (tp);

    case FUNCTION_PROTO_DECL:	/* ANSI type decl */
    case FUNCTION_DECL:
	tq = new_type_desc(FUNCTION_DECL);	/* alloc func-decl type */
	if(EXPR_CODE(decl) == FUNCTION_PROTO_DECL){
	    TYPE_IS_FUNC_PROTO(tq) = TRUE;
	    TYPE_FUNC_PARAMS(tq) = 
		compile_prototype_param_list(EXPR_ARG2(decl));
	} else 
	    TYPE_FUNC_PARAMS(tq) = EXPR_ARG2(decl);

	TYPE_REF(tq) = tp;
	TYPE_SIZE(tq) = 0;	/* size is 0 */
	TYPE_ALIGN(tq) = 0;
	tp = compile_declarator_type(EXPR_ARG1(decl), tq);
	return (tp);

    case ARRAY_DECL:
	tq = new_type_desc(ARRAY_DECL);	/* alloc array-decl type */

	TYPE_REF(tq) = tp;
	TYPE_ARRAY_DIM(tq) = 0;	/* default */

	dim = EXPR_ARG2(decl);	/* evaluate dim expr */
	if ( dim == NULL ){	/* null dimension */
#ifdef not
	    if (EXPR_ARG1(decl) != NULL)
	        if ( EXPR_CODE(EXPR_ARG1(decl)) != IDENT )
		    error_at_node(decl, "null dimension");
#endif
	    /* not checked here */;
	} else {
	    v = eval_expression(dim);
	    if ( v == NULL ){
		return (error_type);
	    }
	    switch (EXPV_CODE(v)) {
	    case INT_CONSTANT:
		TYPE_ARRAY_DIM(tq) = EXPV_INT_VALUE(v);	
		break;
	    case LONG_CONSTANT: {
		unsigned long lVal = (unsigned long)EXPV_LINT_VALUE(v);
		if (lVal > UINT_MAX) {
		    error_at_node(dim, "size of array out of range");
		} else {
		    TYPE_ARRAY_DIM(tq) = (unsigned int)lVal;
		    break;
		}
	    }
	    case LONGLONG_CONSTANT:
		if (EXPV_LLINT_HIGH(v) != 0) {
		    error_at_node(dim, "size of array out of range");
		} else {
		    TYPE_ARRAY_DIM(tq) = EXPV_LLINT_LOW(v);
		}
		break;
	    case MOE_CONSTANT:
		id = lookup_ident(v);
		if(id != NULL && ID_CLASS(id) == MOE){
		    TYPE_ARRAY_DIM(tq) = EXPV_INT_VALUE(ID_BASE(id));
		} else {
		    fatal ("can not find enum identifier.");
		}
		break;
	    case CAST_EXPR:
	        /* not implement */
	    default:
		error_at_node(dim, "size of array is not integer constant");
		break;
	    }
	}

	TYPE_SIZE(tq) = TYPE_SIZE(TYPE_REF(tq)) * TYPE_ARRAY_DIM(tq);
	TYPE_ALIGN(tq) = TYPE_ALIGN(TYPE_REF(tq));
	tp = compile_declarator_type(EXPR_ARG1(decl), tq);
	return (tp);

    default:
	fatal("compile_declarator_type");
	return NULL;
    }
}


/* convert (LIST arg1 arg2 ...), if f(void) -> (LIST)
 * type name -> (LIST:type name) 
 * type      -> (LIST:type)
 * ...       -> ()) 
 */
static expv compile_prototype_param_list(expr param_decl_list)
{
    list lp;
    TYPE_DESC type;
    expr name,param_list,decl;

    param_list = list0(LIST);	/* list head */
    if(EXPR_LIST(param_decl_list) != NULL && 
       LIST_NEXT(EXPR_LIST(param_decl_list)) == NULL){
	/* f(void), void alone is acceptable.. */
	decl = LIST_ITEM(EXPR_LIST(param_decl_list));
	name = EXPR_ARG2(decl);
	type = compile_decl_type_spec(EXPR_ARG1(decl));
	if(name == NULL && IS_VOID(type)) return param_list;
    }

    FOR_ITEMS_IN_LIST(lp, param_decl_list) {
	decl = LIST_ITEM(lp);
	if (decl == NULL) {	/* this is "..." */
	    if (LIST_NEXT(lp) != NULL)
		error_at_node(param_decl_list, "syntax error at \"...\"");
	    param_list = list_put_last(param_list,NULL);
	    break;
	}
	type = compile_decl_type_spec(EXPR_ARG1(decl));
	type = compile_declarator_type(EXPR_ARG2(decl),type);
	type = combine_type_qual(EXPR_ARG1(decl),type);

	name = declarator_name(EXPR_ARG2(decl));
	if(name != NULL) name = expv_sym_term(IDENT,type,EXPR_SYM(name));
	else name = expv_cons(LIST,type,NULL,NULL);
	param_list = list_put_last(param_list,name);
    }
    return param_list;
}

/* make new type table */
/* allocate TYPE_DESC for pointer type */
TYPE_DESC pointer_type(TYPE_DESC tp)
{
    TYPE_DESC tq;

    tq = new_type_desc(POINTER_DECL);
    TYPE_REF(tq) = tp;
    TYPE_SIZE(tq) = pointer_type_size;
    TYPE_ALIGN(tq) = pointer_type_align;
    return (tq);
}

/* allocate TYPE_DESC for function declaration */
TYPE_DESC func_type(TYPE_DESC tp)
{
    TYPE_DESC tq;

    tq = new_type_desc(FUNCTION_DECL);
    TYPE_REF(tq) = tp;
    TYPE_SIZE(tq) = 0;
    TYPE_ALIGN(tq) = 0;
    return (tq);
}

/* allocate TYPE_DESC for the same type(copy) */
TYPE_DESC duplicate_type(TYPE_DESC tp)
{
    TYPE_DESC tq;

    tq = new_type_desc(BASIC_TYPE_NODE);
    bcopy(tq, tp, sizeof(*tp));		/* duplicate */
    return (tq);
}

/* extract identifier from declarator expr */
static expr    declarator_name(expr    decl)
{
    expr  v;

    if ( decl == NULL )
	return (NULL);
    else if ( EXPR_CODE(decl) == IDENT )
	return (decl);
    else{
	v = declarator_name(EXPR_ARG1(decl));
	return (v);
    }
}

/* just look for name and return its ID pointer, return NULL if not found */
ID lookup_ident(expr    name)
{
    SYMBOL  sp;
    ID      id;
    int     lev;

    if ( name == NULL )
	return (NULL);

    sp = EXPR_SYM(name);
    for ( lev = current_level ; lev >= 0 ; lev-- ){
	for ( id = id_lists[lev] ; id != NULL ; id = ID_NEXT(id) )
	    if ( ID_CLASS(id) != TAGNAME && ID_SYM(id) == sp )
		return (id);
    }

    return (NULL);	/* not found */
}

static void check_prev_function_prototype(ID id,TYPE_DESC tp,
				   expr name /* for error message */)
{
    if(IS_FUNCTION(tp) &&
       !TYPE_IS_FUNC_PROTO(ID_TYPE(id)) &&  TYPE_IS_FUNC_PROTO(tp)){
	TYPE_IS_FUNC_PROTO(ID_TYPE(id)) = TRUE;
	TYPE_FUNC_PARAMS(ID_TYPE(id)) = TYPE_FUNC_PARAMS(tp);
	if(ID_CLASS(id) == EXTDEF || ID_CLASS(id) == STATIC){
	    warning_at_node(name,"prototype for `%s' follows non-prototype definition",ID_NAME(id));
	}
    }
}

/* define symbol with specified class in current enviroment.
 * and return ID(identifier descriptor). */
ID      define_ident(expr    name, STORAGE_CLASS class, TYPE_DESC tp)
{
    SYMBOL  sp;
    ID      id = NULL;
    ID      idt;
    TYPE_DESC tp1;
    int     lev, fflag;
    int     defined_level = -INT_MAX;

    if ( name == NULL )
	return (NULL);	/* nothing to do */

    if ( tp != NULL )
	check_type_combination(tp, name);

    sp = EXPR_SYM(name);
	/* search current environment */
    for( lev = current_level, fflag = 0 ; lev >= 0 ; lev-- ){
	for( id = id_lists[lev] ; id != NULL ; id = ID_NEXT(id) ){
	    if ( ID_CLASS(id) != TAGNAME && ID_SYM(id) == sp ){
		defined_level = lev;
		fflag = 1;
		break;
	    }
	}
	if ( fflag > 0 )
	    break;
    }

    if ( fflag == 0 )	/* not found */
	goto define_it;

    /* ---found case---
     * if symbol is already defined or referenced, check combination with the
     * previous definition. 
     */
    /* At first, their types must be the same. */
    if ( type_equal(ID_TYPE(id), tp) ){
	switch (class) {
	case EXTERN:
	    switch (ID_CLASS(id)) {
	    case STATIC:
		/* if already defined as "static" at level-0, the
		 * following "extern" does not change its class */
		if ( defined_level == 0 ){
		    check_prev_function_prototype(id,tp,name);
		    if(defined_level != current_level) goto define_it;
		    return (id);
		}
		break;

	    case EXTDEF:
	    case EXTERN:
		/* if already defined as external, "extern" have no effect. */
		check_prev_function_prototype(id,tp,name);
		if(defined_level != current_level) goto define_it;
		return (id);
	    default: 
		break;
	    }
	    break;

	case EXTDEF:
	    if (ID_CLASS(id) == EXTERN || 
		(ID_CLASS(id) == EXTDEF && !IS_FUNCTION(tp))){
		/* define id already referenced as "extern" */
		/* previous array definition was dummy */
		if ( IS_ARRAY(tp) && TYPE_ARRAY_DIM(tp) != 0 ){
		    TYPE_ARRAY_DIM(ID_TYPE(id)) = TYPE_ARRAY_DIM(tp);
		    TYPE_SIZE(ID_TYPE(id)) = TYPE_SIZE(tp);
		}
		check_prev_function_prototype(id,tp,name);
		ID_CLASS(id) = EXTDEF;
		ID_TYPE(id) = tp;	/* replace with defined type */
		return (id);
	    }
	    break;

	case STATIC:
	    if ( ID_CLASS(id) == EXTERN && current_level == 0 ){
		/* define "static" id referenced as "extern" */
		warning_at_node(name,"static declaration for `%s' follows non-static",ID_NAME(id));
		check_prev_function_prototype(id,tp,name);
		ID_CLASS(id) = STATIC;
		return (id);
	    }
	    break;

	case TYPEDEF_NAME:
	    if (ID_CLASS(id) == TYPEDEF_NAME)
		return (id);
	    break;

	case LABEL:
	    if (ID_CLASS(id) == LABEL){
		error_at_node(name, "redeclaration label of '%s'", ID_NAME(id));
		return (id);
	    }
	    else if ( ID_CLASS(id) == ULABEL ){
		ID_CLASS(id) = LABEL;
		return (id);
	    }
	    break;

	case ULABEL:
	    if ( ID_CLASS(id) == ULABEL || ID_CLASS(id) == LABEL )
		return (id);
	    break;

	default:
	    break;
	}
    }

    /* mismatch */
    if(class == EXTERN && /* IS_FUNCTION(tp) &&*/ 
       current_level != GLOBAL_LEVEL){
	/* check external function declared in local context */
	for(idt = id_lists[GLOBAL_LEVEL] ; idt != NULL ; idt = ID_NEXT(idt)){
	    if (ID_CLASS(idt) != TAGNAME && ID_SYM(idt) == sp){
		if(!type_equal(ID_TYPE(idt),tp)) goto mismatch;
		check_prev_function_prototype(idt,tp,name);
		break;
	    }
	}
	goto define_it;
    }

mismatch:
    if ( class == EXTERN || defined_level == current_level ){
	if (!(class == STATIC && IS_FUNCTION(tp))){
	    error_at_node(name, "redeclaration of '%s'", ID_NAME(id));
	}
	return (id);
    }

define_it:
	/* define current definition level */
    if ( fflag > 0 ){	/* fall through case? */
	if(ID_CLASS(id) == PARAM && current_level == (param_level+1))
	    warning_at_node(name,"declaration of '%s' hides aparameter",
			    ID_NAME(id));
	defined_level = current_level;
    } else if ( class == LABEL || class == ULABEL )
	defined_level = label_level;
/*    else if ( class == EXTERN )
	defined_level = GLOBAL_LEVEL;*/
    else
	defined_level = current_level;

	/* make new entry: insert tail! of id_lists[lev] */
    id = new_ident_desc(sp);

    /* put new id at last */
    if ( id_lists[defined_level] == NULL ){
	id_lists[defined_level] = id;
    } else {
	for(idt = id_lists[defined_level]; ID_NEXT(idt) != NULL; 
	    idt = ID_NEXT(idt) ) /* */;
	ID_NEXT(idt) = id;
    }

    ID_CLASS(id) = class;
    ID_TYPE(id) = tp;

    if ( debug_X_flag2 ){
	dump_id_list(defined_level);
    }
    if ( debug_flag2 ){
	if ( tp->aux.params != NULL && (tp->code == FUNCTION_DEFINITION ||
	   tp->code == FUNCTION_DECL || tp->code == FUNCTION_PROTO_DECL) ){
	    fprintf(debug_fp, "----------------------------------\n");
	    expr_print(tp->aux.params, debug_fp);
	    fprintf(debug_fp, "----------------------------------\n");
	}
    }

    switch (class) {
    case AUTO:			/* auto variable */
    case REGISTER:
	ID_HIDE(id) = fflag;
	if (TYPE_SIZE(tp) == 0){
	    if(!ID_ERROR(id) && !IS_UNDEF(tp)){
		error_at_node(name, "storage size of '%s' is unknown", ID_NAME(id));
	    }
	    ID_ERROR(id) = TRUE;
	    return (id);
	}
	/* allocate auto */
	if ( IS_ARRAY(ID_TYPE(id)) )
	    ID_BASE(id) = expv_sym_term(LARRAY_ADDR, ID_TYPE(id), ID_SYM(id));
	else if ( IS_FUNCTION(ID_TYPE(id)) )
	    fatal("local function ???");
	else{
	    tp1 = pointer_type(ID_TYPE(id));
	    ID_BASE(id) = expv_sym_term(LVAR_ADDR, tp1, ID_SYM(id));
	}
	break;

    case STATIC:
    case EXTERN:
    case EXTDEF:
    case MEMBER:
	/* set base expression */
	if (IS_ARRAY(ID_TYPE(id))){
	    /* tp1 = pointer_type(ID_TYPE(id)); *//* bug??*/
	    tp1 = ID_TYPE(id);
	    ID_BASE(id) = expv_sym_term(ARRAY_ADDR, tp1, ID_SYM(id));
	}
	else if (IS_FUNCTION(ID_TYPE(id))){
	    tp1 = pointer_type(ID_TYPE(id));
	    ID_BASE(id) = expv_sym_term(FUNC_ADDR, tp1, ID_SYM(id));
	}
	else{
	    tp1 = pointer_type(ID_TYPE(id));
	    ID_BASE(id) = expv_sym_term(VAR_ADDR, tp1, ID_SYM(id));
	}
	break;

    case LABEL:
    case ULABEL:
	ID_LABEL_IS_DEFINED(id) = TRUE;
	break;
    case PARAM:
    case TAGNAME:
    case MOE:
    case TYPEDEF_NAME:
    case SNULL: /* ? */
	break;
    default:
	fatal("define_ident: unknown class %d\n",class);
    }

    return (id);
}

/* define TAG_NAME in current environment. */
/* If def_flag is TRUE, it must be undefined. If def_flag is FALSE and
 * undefined, then make referenced entry */
static TYPE_DESC define_tag_name(expr    name,
    enum expr_code code,
    int     def_flag	/* declaration(true) or refered(false) */)
{
    SYMBOL  sp;
    TYPE_DESC tp;
    ID      id = NULL;
    ID      idt;
    int     lev, fflag;
    int     defined_level = -INT_MAX;

    fflag = 0;
    if (name == NULL) {	/* if no name, return a new descriptor */
	sp = NULL;	/* define tagname with NULL id */
    } else {	/* search name in 'id_lists' */
	sp = EXPR_SYM(name);
	for (lev = current_level ; lev >= 0 ; lev--){
	    for (id = id_lists[lev] ; id != NULL ; id = ID_NEXT(id)){
		if ( ID_CLASS(id) == TAGNAME && ID_SYM(id) == sp ){
		    defined_level = lev;
		    fflag = 1;	/* find */
		    break;
		}
	    }
	    if ( fflag > 0 ){
		/* if def_flag == TRUE, then override this definition. */
		if (def_flag && defined_level != current_level)
		    fflag = 0;
		break;
	    }
	}
    }

    if ( fflag > 0 ){	/* find */
	/* check type  and defined or undefined (TYPE_SIZE(tp) == 0) */
	tp = ID_TYPE(id);
	if ( TYPE_DESC_CODE(tp) != code || (def_flag && TYPE_SIZE(tp) != 0) )
	    error_at_node(name, "redeclaration of '%s'", SYM_NAME(sp));
	return (tp);
    }
    else{    /* not found */
	/* make new entry at current: insert id_lists[lev] tail! */
	id = new_ident_desc(sp);

	if ( id_lists[current_level] == NULL ){
	    id_lists[current_level] = id;
	}
	else{
	    for( idt = id_lists[current_level] ; ; idt = ID_NEXT(idt) ){
		if ( ID_NEXT(idt) == NULL ){
		    ID_NEXT(idt) = id;	/* link at 'id_lists' tail */
		    break;
		}
	    }
	}

	ID_CLASS(id) = TAGNAME;
	tp = new_type_desc(code);	/* also make new type table */
	ID_TYPE(id) = tp;
	TYPE_TAG_NAME(tp) = id;

	if ( debug_X_flag2 ){	/* dump ID list of current level */
	    dump_id_list(current_level);
	}

	return (tp);
    }
}

/* combine multiple type to one type */
TYPE_DESC combine_basic_type(expr    x, TYPE_DESC tp)
{
    BASIC_DATA_TYPE t1, t2, t;

    if (EXPR_CODE(x) == TYPE_QUAL_NODE) {	/* type qualifier node */
	return tp;	/* skip */
    }

    if (EXPR_CODE(x) != BASIC_TYPE_NODE)
	goto err;
    t1 = EXPR_TYPE(x);
    if (TYPE_DESC_CODE(tp) != BASIC_TYPE_NODE)
	goto err;
    t2 = TYPE_BASIC_TYPE(tp);

    t = UNDEF;
    if (t1 == UNDEF || t2 == UNDEF)
	goto ok;		/* don't care */

    switch (t1) {
    default:
	goto err;

    case UNSIGNED:
	switch (t2) {
	case CHAR:
	    t = UNSIGNED_CHAR;
	    goto ok;
	case INT:
	    t = UNSIGNED;
	    goto ok;
	case SHORT:
	    t = UNSIGNED_SHORT;
	    goto ok;
	case LONG:
	    t = UNSIGNED_LONG;
	    goto ok;
	case LONGLONG:
	    t = UNSIGNED_LONGLONG;
	    goto ok;
	default: 
	    break;
	}
	break;

    case SIGNED:
	switch (t2) {
	case UNSIGNED_CHAR:
	case UNSIGNED:
	case UNSIGNED_LONG:
	case UNSIGNED_LONGLONG:
	    goto err;
	case CHAR:
	    t = CHAR;
	    goto ok;
	case INT:
	    t = INT;
	    goto ok;
	case SHORT:
	    t = SHORT;
	    goto ok;
	case LONG:
	    t = LONG;
	    goto ok;
	case LONGLONG:
	    t = LONGLONG;
	    goto ok;
	default:
	    break;
	}
	break;

    case LONG:			/* size spec */
	switch (t2) {
	case UNSIGNED:
	    t = UNSIGNED_LONG;
	    goto ok;
	case FLOAT:
	    t = DOUBLE;
	    goto ok;
	case LONG:
	    t = LONGLONG;
	    goto ok;
	case UNSIGNED_LONG:
	    t = UNSIGNED_LONGLONG;
	    goto ok;
	case INT:
	    t = LONG;
	    goto ok;
	case DOUBLE:
	    t = LONG_DOUBLE;
	    goto ok;
	default:
	    break;
	}
	break;

    case SHORT:		/* size spec */
	switch (t2) {
	case UNSIGNED:
	    t = UNSIGNED_SHORT;
	    goto ok;
	case INT:
	    t = SHORT;
	    goto ok;
	default: 
	    break;
	}
	break;

    case INT: 
	switch (t2) {
	case UNSIGNED:
	    t = UNSIGNED;
	    goto ok;
	case SIGNED:
	    t = INT;
	    goto ok;
	case SHORT:
	    t = SHORT;
	    goto ok;
	default: 
	    break;
	}
	break;

    }

err:
    error_at_node(x, "illegal type combination");
    t = UNDEF;
ok:
    tp = new_type_desc(BASIC_TYPE_NODE);
    TYPE_BASIC_TYPE(tp) = t;
    TYPE_SIZE(tp) = basic_type_size(t);
    TYPE_ALIGN(tp) = basic_type_align(t);
    return (tp);
}

TYPE_DESC combine_type_qual(expr x, TYPE_DESC tp)
{
    TYPE_DESC tq;

    if(x == NULL) return tp;

    if (EXPR_CODE(x) == TYPE_QUAL_NODE) {	/* type qualifier node */
	tq = new_type_desc(BASIC_TYPE_NODE);
	bcopy(tp, tq, sizeof(*tp));

	switch (EXPR_TYPE_QUAL(x)) {
	case QUAL_CONST:
	    if (IS_ENUM(tp) || IS_UNION(tp) || IS_STRUCT(tp)) {
	      TYPE_MASTER_NODE(tq) = tp;
	    }
	    TYPE_IS_CONST(tq) = TRUE;
	    break;

	case QUAL_VOLATILE:
	    if (IS_ENUM(tp) || IS_UNION(tp) || IS_STRUCT(tp)) {
	      TYPE_MASTER_NODE(tq) = tp;
	    }
	    TYPE_IS_VOLATILE(tq) = TRUE;
	    break;

	default:
	    return tp;
	}
	return (tq);
    } /* else if(EXPR_CODE(x) == LIST){
	return combine_type_qual(EXPR_ARG1(x),tp);
    }  */
    return tp;
}

static int have_QUAL_INLINE(expr x)
{
    if(x == NULL) return FALSE;

    if (EXPR_CODE(x) == TYPE_QUAL_NODE) { /* type qualifier node */
	if(EXPR_TYPE_QUAL(x) == QUAL_INLINE) return TRUE;
    } else if(EXPR_CODE(x) == LIST){
	return have_QUAL_INLINE(EXPR_ARG1(x));
    } 
    return FALSE;
}

/* compare type between TYPE 'tp' and variable 'name' */
void check_type_combination(TYPE_DESC tp,
    expr    name	/* for error message */)
{
    TYPE_DESC tq;

	/* check types */
    if (TYPE_DESC_CODE(tp) == BASIC_TYPE_NODE && TYPE_BASIC_TYPE(tp) == UNDEF)
	return;

    for (tq = tp ; tq != NULL ; tq = TYPE_REF(tq)) {
	if (TYPE_DESC_CODE(tq) == POINTER_DECL)
	    continue;
	else if (TYPE_DESC_CODE(tq) == ARRAY_DECL) {
	    if (TYPE_DESC_CODE(TYPE_REF(tq)) == FUNCTION_DECL)
		error_at_node(name, "array of functions is illegal");
	}
	else if (TYPE_DESC_CODE(tq) == FUNCTION_DECL) {
	    if (TYPE_DESC_CODE(TYPE_REF(tq)) == ARRAY_DECL ||
			TYPE_DESC_CODE(TYPE_REF(tq)) == FUNCTION_DECL)
		error_at_node(name, "function returns illegal type");
	}
	else
	    break;		/* else atomic */
    }
}

/* align? */
void align_offset(TYPE_DESC tp,int *offset)
{
    int     align;

    align = TYPE_ALIGN(tp);
    if ( align != 0 )
	*offset = ROUND(*offset, align);
}

/* check for arg(tp) is basic type or enum
 * TRUE if 'tp' is char, short, int, long or ENUM, others are FALSE */
int is_integral_type(TYPE_DESC tp)
{
    if (TYPE_DESC_CODE(tp) == BASIC_TYPE_NODE) {
	switch (TYPE_BASIC_TYPE(tp)) {
	case CHAR:
	case SHORT:
	case INT:
	case LONG:
	case LONGLONG:
	case UNSIGNED_CHAR:
	case UNSIGNED_SHORT:
	case UNSIGNED:
	case UNSIGNED_LONG:
	case UNSIGNED_LONGLONG:
	    return (TRUE);
	default:
	    break;
	}
    }
    else if (TYPE_DESC_CODE(tp) == ENUM_TYPE)
	return (TRUE);

    return (FALSE);
}

/* check for unsigned type */
int is_unsigned_type(TYPE_DESC tp)
{
    if (IS_POINTER(tp))
	return (TRUE);		/* pointer also unsigned */

    if (TYPE_DESC_CODE(tp) == BASIC_TYPE_NODE) {
	switch (TYPE_BASIC_TYPE(tp)) {
	case UNSIGNED_CHAR:
	case UNSIGNED_SHORT:
	case UNSIGNED:
	case UNSIGNED_LONG:
	case UNSIGNED_LONGLONG:
	    return (TRUE);
	default: 
	    break;
	}
    }

    return (FALSE);
}

/* check for floating type */
int is_float_type(TYPE_DESC tp)
{
    if (TYPE_DESC_CODE(tp) == BASIC_TYPE_NODE) {
	switch (TYPE_BASIC_TYPE(tp)) {
	case FLOAT:
	case DOUBLE:
	case LONG_DOUBLE:
	    return (TRUE);
	default:
	    break;
	}
    }

    return (FALSE);
}

/* the same tyep: TRUE(1), different type: FALSE(0) */
int  type_equal(TYPE_DESC tp, TYPE_DESC tq)
{
#ifdef not
    ID      idp, idq;
#endif
    list lp,lq;

    if (tp == tq)
	return (TRUE);

    if (tp == NULL || tq == NULL)
	return (FALSE);

    if ( (TYPE_DESC_CODE(tp) == POINTER_DECL &&
	  TYPE_DESC_CODE(tq) == ARRAY_DECL) ||
	 (TYPE_DESC_CODE(tq) == POINTER_DECL &&
	  TYPE_DESC_CODE(tp) == ARRAY_DECL)){
	return( type_equal(TYPE_REF(tp), TYPE_REF(tq)) );
    }

    if (TYPE_DESC_CODE(tp) != TYPE_DESC_CODE(tq))
	return (FALSE);

    switch (TYPE_DESC_CODE(tp)){
    case BASIC_TYPE_NODE:
#ifdef INT_IS_LONG
	if((TYPE_BASIC_TYPE(tp) == INT && TYPE_BASIC_TYPE(tq) == LONG)||
	   (TYPE_BASIC_TYPE(tq) == INT && TYPE_BASIC_TYPE(tp) == LONG))
	    return TRUE;
	if((TYPE_BASIC_TYPE(tp) == UNSIGNED && 
	    TYPE_BASIC_TYPE(tq) == UNSIGNED_LONG)||
	   (TYPE_BASIC_TYPE(tq) == UNSIGNED && 
	    TYPE_BASIC_TYPE(tp) == UNSIGNED_LONG))
	    return TRUE;
#endif
	return (TYPE_BASIC_TYPE(tp) == TYPE_BASIC_TYPE(tq));

    case FUNCTION_DECL:
	if(!type_equal(TYPE_REF(tp), TYPE_REF(tq))) return FALSE;
	/* if neither have protype, ok */
	if(!TYPE_IS_FUNC_PROTO(tp) || !TYPE_IS_FUNC_PROTO(tq))
	    return TRUE;
	/* check match prototype */
	lp = EXPR_LIST(TYPE_FUNC_PARAMS(tp));
	lq = EXPR_LIST(TYPE_FUNC_PARAMS(tq));
	for( ; lp != NULL && lq != NULL; 
	     lp = LIST_NEXT(lp), lq = LIST_NEXT(lq)){
	    if(LIST_ITEM(lp) == LIST_ITEM(lq))	continue; /* NULL case */
	    if(LIST_ITEM(lp) == NULL || LIST_ITEM(lq) == NULL)
		return FALSE;
	    if(!type_equal(EXPV_TYPE(LIST_ITEM(lp)),
			   EXPV_TYPE(LIST_ITEM(lq)))) break;
	}
	if(lp != NULL || lq != NULL) return FALSE;
	return TRUE;

    case POINTER_DECL:
	return ( type_equal(TYPE_REF(tp), TYPE_REF(tq)) );

    case ARRAY_DECL:		/* array type */
	return ( ( TYPE_ARRAY_DIM(tp) == 0 || TYPE_ARRAY_DIM(tq) == 0 ||
		 TYPE_ARRAY_DIM(tp) == TYPE_ARRAY_DIM(tq) ) &&
		type_equal(TYPE_REF(tp), TYPE_REF(tq)) );

    case STRUCT_TYPE:
    case UNION_TYPE:
	if ( TYPE_SIZE(tp) != TYPE_SIZE(tq) )
	    return (FALSE);
	if(TYPE_MEMBER_LIST(tp) == TYPE_MEMBER_LIST(tq)) return TRUE;
#ifdef not
	for( idp = TYPE_MEMBER_LIST(tp), idq = TYPE_MEMBER_LIST(tq) ;
	     idq != NULL && idp != NULL ;
	     idp = ID_NEXT(idp), idq = ID_NEXT(idq) ){
	    if ( ID_SYM(idp) != ID_SYM(idq) )
		return (FALSE);
	    if ( !type_equal(ID_TYPE(idp), ID_TYPE(idq)) )
		return (FALSE);
	}
	if ( idp == NULL && idq == NULL )
	    return (TRUE);
	else
#endif
	    return (FALSE);

    case ENUM_TYPE:
	if(TYPE_MEMBER_LIST(tp) == TYPE_MEMBER_LIST(tq)) return TRUE;
#ifdef not
	for( idp = TYPE_MEMBER_LIST(tp) , idq = TYPE_MEMBER_LIST(tq) ;
		idq != NULL && idp != NULL ;
		idp = ID_NEXT(idp), idq = ID_NEXT(idq) ){
	    if ( ID_SYM(idp) != ID_SYM(idq) )
		return (FALSE);
#ifdef xxx			/* ???? */
	    if (ID_OFFSET(idp) != ID_OFFSET(idq))
		return (FALSE);
#endif
	}
	if ( idp == NULL && idq == NULL )
	    return (TRUE);
	else
#endif
	    return (FALSE);

    default:	/* as default, these must be the same struct/union */
	return (FALSE);
    }
}

/* compatiblity ingores sign/unsigned */
int is_compatible_type(TYPE_DESC tp,TYPE_DESC tq)
{
    BASIC_DATA_TYPE btype;

    if ( tp == NULL || tq == NULL )
	return (FALSE);

    if ( type_equal(tp, tq) ){	/* exact the same type. */
	if (TYPE_DESC_CODE(tp) == FUNCTION_DECL )
	    return (FALSE);	/* for FUNCITON, never compatible */
	else
	    return (TRUE);	/* exact same */
    }

	/* (void *) is compatible to any pointer */
    if ( TYPE_DESC_CODE(tp) == POINTER_DECL &&
	 TYPE_DESC_CODE(tq) == POINTER_DECL &&
	 ( IS_VOID(TYPE_REF(tp)) || IS_VOID(TYPE_REF(tq)) ) )
	return (TRUE);

    if ( TYPE_DESC_CODE(tp) != TYPE_DESC_CODE(tq) )
	return (FALSE);

    if ( TYPE_DESC_CODE(tp) == BASIC_TYPE_NODE ){
	btype = TYPE_BASIC_TYPE(tq);
	switch ( TYPE_BASIC_TYPE(tp) ){
	case CHAR:
	case UNSIGNED_CHAR:
	    return (btype == CHAR || btype == UNSIGNED_CHAR);

	case SHORT:
	case UNSIGNED_SHORT:
	    return (btype == SHORT || btype == UNSIGNED_SHORT);

	case INT:
	case SIGNED:
	case UNSIGNED:
#ifdef INT_IS_LONG
	    if(btype == LONG || btype == UNSIGNED_LONG) return TRUE;
#endif
	    return (btype == INT || btype == UNSIGNED || btype == SIGNED);

	case LONG:
	case UNSIGNED_LONG:
#ifdef INT_IS_LONG
	    if(btype == INT || btype == UNSIGNED || btype == SIGNED) 
		return TRUE;
#endif
	    return (btype == LONG || btype == UNSIGNED_LONG);

	case LONGLONG:
	case UNSIGNED_LONGLONG:
	    return (btype == LONGLONG || btype == UNSIGNED_LONGLONG);
	default:
	    break;
	}
    }

    return (FALSE);
}

/* change position of the specified type's ID table to the last one */
void move_id_last( TYPE_DESC  type,
    expr  decl	/* struct/union/enum type expr(parser) */)
{
    ID  idt, idp;
    ID  idp2 = NULL;
    ID  lastp = NULL;
    int  flag = 0;

    if ( type == NULL || decl == NULL )
	return;

    if ( EXPR_ARG2(decl) == NULL )	/* type body is not here */
	return;

    idt = type->p.tagname;
    if ( idt == NULL )
	return;
    if ( idt->class != TAGNAME ){
	printf("--> wrong type? for move_id_last\n");
	/*error_at_node(decl, "bad declaration"); ? */
	return;
    }

	/* search type's ID and last ID from id_lists[current_level] */
    if ( id_lists[current_level] == idt )
	flag =2;
    for( idp = id_lists[current_level] ; idp != NULL ; idp = ID_NEXT(idp) ){
	lastp = idp;
	if ( idp->ip_next == idt ){
	    flag = 1;
	    idp2 = idp;	/* ...->idp2->idt->...->lastp */
	}
    }

    if ( flag == 0 )	/* cannot find */
	return;

    if ( lastp == idt ){	/* already last one */
	if ( debug_X_flag2 )
	    printf("----<<not exchange ID>>----\n");
	return;
    }
	/* move specified ID table to the last */
    if ( flag == 1 ){
	idp2->ip_next = idt->ip_next;
    }
    else if ( flag == 2 ){	/* list head case */
	id_lists[current_level] = id_lists[current_level]->ip_next;
    }
    lastp->ip_next = idt;
    idt->ip_next = NULL;

    if ( debug_X_flag2 ){	/* dump ID list of current level */
	printf("----<<exchange the ID order>>----\n");
	dump_id_list(current_level);
	printf("----<<--------------------->>----\n");
    }
}

/*
 * For debug output:
 */
/* print ID info */
void print_ID(ID id, FILE *fp)
{
    fprintf(fp, "ID Symbol name[");
    if ( ID_SYM(id) != NULL )
	fprintf(fp, "%s", ID_NAME(id));
    else
	fprintf(fp, "  ");
    fprintf(fp, "]  Storage class[%s]\n", storage_class_name(ID_CLASS(id)));

    fprintf(fp, "\tType = ");
    print_type_name(ID_TYPE(id), fp);
    fprintf(fp, "\n");

    fprintf(fp, "\tBase = ");
    X_output(ID_BASE(id), fp);	/* expression output */
}

/* print specified level ID list */
void dump_id_list(int  lev)
{
    ID      id;
    int  i;

    fprintf(debug_fp, "*** id list(level %d) dump ***\n", lev);
    for( id = id_lists[lev], i = 0 ; id != NULL ; id = ID_NEXT(id), i++ ){
	fprintf(debug_fp, "ID table[%d]-----------\n", i);
	print_ID(id, debug_fp);
	fprintf(debug_fp, "--------------------------------\n");
    }
    fprintf(debug_fp, "*** id list(level %d) end ***\n", lev);
}

void print_id_list(char *msg, ID id_list,FILE *fp)
{
    ID id;
    if(msg != NULL) fprintf(fp,"%s:",msg);
    fprintf(fp,"[");
    for(id = id_list; id != NULL; id = ID_NEXT(id)){
	fprintf(fp," %s:%s",ID_NAME(id),storage_class_name(ID_CLASS(id)));
    }
    fprintf(fp,"]\n");
}

/* print whole type structure */
void print_type(TYPE_DESC tp, FILE   *fp)
{
    ID      id;

    fprintf(fp, "{%s[%d byte] ", EXPR_CODE_NAME(TYPE_DESC_CODE(tp)),
		TYPE_SIZE(tp));

    if (IS_BASIC_TYPE(tp))
	fprintf(fp, " %s", basic_type_name(TYPE_BASIC_TYPE(tp)));

    else if (IS_POINTER(tp) || IS_ARRAY(tp) || IS_FUNCTION(tp))
	print_type(TYPE_REF(tp), fp);

    else if ( IS_STRUCT(tp) || IS_UNION(tp) ){
	if (TYPE_TAG_NAME(tp) != NULL){
	    if ( ID_SYM(TYPE_TAG_NAME(tp)) != NULL )
		fprintf(fp, "tag-name[%s]", ID_NAME(TYPE_TAG_NAME(tp)));
	    else
		fprintf(fp, "--no-tag");
	} else
	    fprintf(fp, "--no-tag");

	if (TYPE_MEMBER_LIST(tp) != NULL){	/* print member */
	    fprintf(fp, "\n");
	    for ( id = TYPE_MEMBER_LIST(tp) ; id != NULL ; id = ID_NEXT(id) ){
		fprintf(fp, "\telement[%s], type = ", ID_NAME(id));
		print_type_name(ID_TYPE(id), fp);
		fprintf(fp, "\n");
	    }
	    fprintf(fp, "\t");
	}
    }
    else if (IS_ENUM(tp)) {
	if (TYPE_TAG_NAME(tp) != NULL){
	    if ( ID_SYM(TYPE_TAG_NAME(tp)) != NULL )
		fprintf(fp, "tag-name[%s]", ID_NAME(TYPE_TAG_NAME(tp)));
	    else 
		fprintf(fp, "--no-tag");
	} else
	    fprintf(fp, "--no-tag");

	if ( TYPE_MOE_LIST(tp) != NULL){	/* print enum member */
	    fprintf(fp, "\n\t\tmember = \n");
	    X_output2(TYPE_MOE_LIST(tp), fp, 8);
	}
    }
    else
	fprintf(fp, "??unknown type??");

    fprintf(fp, "}");
}

void print_type_name(TYPE_DESC tp, FILE   *fp)
{
    fprintf(fp, "{%s[%d byte] Ref[%d] ", EXPR_CODE_NAME(TYPE_DESC_CODE(tp)),
		TYPE_SIZE(tp), tp->is_referenced);

    if (IS_BASIC_TYPE(tp))
	fprintf(fp, " %s", basic_type_name(TYPE_BASIC_TYPE(tp)));

    else if (IS_POINTER(tp) || IS_ARRAY(tp) || IS_FUNCTION(tp))
	print_type_name(TYPE_REF(tp), fp);

    else if ( IS_STRUCT(tp) || IS_UNION(tp) ){
	if (TYPE_TAG_NAME(tp) != NULL){
	    if ( ID_SYM(TYPE_TAG_NAME(tp)) != NULL )
		fprintf(fp, "tag-name[%s]", ID_NAME(TYPE_TAG_NAME(tp)));
	    else
		fprintf(fp, "--no-tag");
	} else
	    fprintf(fp, "--no-tag");
    }
    else if (IS_ENUM(tp)) {
	if (TYPE_TAG_NAME(tp) != NULL){
	    if ( ID_SYM(TYPE_TAG_NAME(tp)) != NULL )
		fprintf(fp, "tag-name[%s]", ID_NAME(TYPE_TAG_NAME(tp)));
	    else
		fprintf(fp, "--no-tag");
	} else
	    fprintf(fp, "--no-tag");
    }
    else
	fprintf(fp, "??unknown type??");

    fprintf(fp, "}");
}


ID *expv_members_list(TYPE_DESC tp)
{
    while (IS_COPY_NODE(tp)) {
	tp = TYPE_MASTER_NODE(tp);
    }

    return &tp->aux.members;
}


expv *expv_moe_list(TYPE_DESC tp)
{
    while (IS_COPY_NODE(tp)) {
	tp = TYPE_MASTER_NODE(tp);
    }

    return &tp->aux.moe_list;
}

static int gen_counter = 0;

/* generate ident */
expr gen_IDENT(char *leader)
{
    SYMBOL sp;
    char buffer[100];
    sprintf(buffer,"_%s_%d",leader,gen_counter++);
    sp = find_symbol(buffer);
    return make_enode(IDENT, (void *)sp);
}

