static char rcsid[] = "$Id: X-io.c,v 1.5 2002/02/27 15:59:02 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.
 *  
 *  
 *  $
 */
/* X-io.c: read/write intermediate structure from/to file */

#include "C-front.h"

char   *basic_data_type_names[] = BASIC_DATA_TYPE_NAMES;
char   *type_qualifier_names[] = TYPE_QUAL_NAMES;
char   *storage_class_names[] = STORAGE_CLASS_NAMES;


/*
 * intermediate structure: (opcode{:type}{@position} operands ...)
 */

/*
 * INPUT
 */
#define XIO_BUF_LEN 256
static FILE *input_fp;
static char input_buf[XIO_BUF_LEN];

#if 0
static expv	X_input _ANSI_ARGS_((FILE *fp));
#endif
static expv	X_input_rec _ANSI_ARGS_((void));
static TYPE_DESC	X_input_type _ANSI_ARGS_((void));
static TYPE_DESC	X_input_find_type _ANSI_ARGS_((int id));
static ID	X_input_id_list _ANSI_ARGS_((void));

static void	collect_type_desc _ANSI_ARGS_((expv v));

static int	X_input_skip_space _ANSI_ARGS_((void));
static int	X_input_int _ANSI_ARGS_((void));
static long	X_input_long _ANSI_ARGS_((void));
static void	X_input_name _ANSI_ARGS_((void));
static void	X_input_str _ANSI_ARGS_((void));

static void	X_output_type _ANSI_ARGS_((TYPE_DESC tp, FILE *fp));
static void	X_output_no_indent _ANSI_ARGS_((expv v, FILE *fp));
static void	X_output_rec _ANSI_ARGS_((expv v, int l));
static void	X_output_id_list _ANSI_ARGS_((ID id_list, FILE *fp));
static void	mark_type_desc _ANSI_ARGS_((TYPE_DESC tp));
static void	mark_type_desc_in_id_list _ANSI_ARGS_((ID idp));
static void	debug_output_exp _ANSI_ARGS_((expv v));
static void	X_output_str_constant _ANSI_ARGS_((char *s, FILE *fp));

static char *	type_qual_name _ANSI_ARGS_((TYPE_QUAL qual));
static void 	expr_print_rec _ANSI_ARGS_((expr x, int l));

/* Read intermediate structure from file */
void input_X_file()
{
    TYPE_DESC tp;
    int     tid;
    int     c;
    expv    v;

    input_fp = source_file;

    /* 1st part, input types: ex. '{P39377 0x4 0x4 0 0 int}' */
    for ( ; ; ) {
	c = X_input_skip_space();
	if (c == '%')
	    break;	/* no more type structure */
	if (c != '{')
	    fatal("input_X_file: bad type section");

	X_input_name();	/* read name(ex. P39718) to input_buf */
	sscanf(&input_buf[1], "%x", &tid);
	tp = X_input_find_type(tid);	/* check type table for 'id' */
	if (TYPE_DESC_CODE(tp) != UNDEF)
	    fatal("input_X_file: redefined type");
	c = input_buf[0];	/* type indicator */

	/* set type member(size, align, constant, volatile) */
	TYPE_SIZE(tp) = X_input_int();
	TYPE_ALIGN(tp) = X_input_int();
	TYPE_IS_CONST(tp) = X_input_int();
	TYPE_IS_VOLATILE(tp) = X_input_int();
	tp->is_referenced = 1;	/* mark it as referenced */

	switch (c) {
	case 'P':	/* pointer */
	    TYPE_DESC_CODE(tp) = POINTER_DECL;
	    TYPE_REF(tp) = X_input_type();
	    break;

	case 'F':	/* function */
	    TYPE_DESC_CODE(tp) = FUNCTION_DECL;
	    TYPE_REF(tp) = X_input_type();
	    TYPE_IS_FUNC_PROTO(tp) = X_input_int();
	    TYPE_FUNC_PARAMS(tp) = X_input_rec();
	    break;

	case 'A':	/* array */
	    TYPE_DESC_CODE(tp) = ARRAY_DECL;
	    TYPE_REF(tp) = X_input_type();
	    TYPE_ARRAY_DIM(tp) = X_input_int();
	    break;

	case 'E':	/* enumerate */
	    TYPE_DESC_CODE(tp) = ENUM_TYPE;
	    TYPE_MOE_LIST(tp) = X_input_rec();
	    break;

	case 'S':	/* struct */
	    TYPE_DESC_CODE(tp) = STRUCT_TYPE;
	    TYPE_MEMBER_LIST(tp) = X_input_id_list();
	    break;

	case 'U':	/* union */
	    TYPE_DESC_CODE(tp) = UNION_TYPE;
	    TYPE_MEMBER_LIST(tp) = X_input_id_list();
	    break;

	default:	/* others */
	    fatal("X_input_file: unknown type tag '%c'", c);
	}

	if (X_input_skip_space() != '}')
	    fatal("X_input_file: bad type desc");
    }

    /* 2nd part, input global id_lists[0] */
    GLOBAL_ID_LIST = X_input_id_list();

    c = X_input_skip_space();
    if (c != '%')
	fatal("X_input_file: bad global id list section");

    /* 3rd part, input declaration and function */
    while ((v = X_input_rec()) != NULL) {
	GLOBAL_DECL_LIST = list_put_last(GLOBAL_DECL_LIST, v);
    }
}

/* Read type: check and generate type structure according to the 'id' */
static TYPE_DESC X_input_find_type(int id)
{
    TYPE_DESC tp;

    for (tp = type_list ; tp != NULL ; tp = TYPE_LINK(tp))
	if (tp->type_id == id)
	    return (tp);

    /* generate new type table and link it to the global list */
    tp = new_type_desc((enum expr_code)UNDEF);
    tp->type_id = id;
    if (type_list == NULL)
	type_list = tp;
    else
	TYPE_LINK(type_list_tail) = tp;
    type_list_tail = tp;
    return (tp);
}

/* check and return type table for input name */
static TYPE_DESC X_input_type()
{
    int     i;

    X_input_name();
    switch (input_buf[0]) {
    case 'P':	/* pointer */
    case 'F':	/* function */
    case 'A':	/* array */
    case 'E':	/* enum */
    case 'S':	/* struct */
    case 'U':	/* union */
	sscanf(&input_buf[1], "%x", &i);	/* read hex */
	return (X_input_find_type(i));	/* return type table for id */

    default:	/* basic type */
	for (i = 0 ; i < N_BASIC_TYPE ; i++)
	    if (strcmp(input_buf, basic_data_type_names[i]) == 0)
		break;
	if (i < N_BASIC_TYPE)
	    return (basic_type_desc[i]);
	else {
	    fatal("X_input_type: bad type '%s'", input_buf);
	    return NULL;
	}
    }
}

/*
 * (2nd) Read global(level 0) ID-list from file
 * Format:: '[ name storage_class type base ]'
 */
static ID  X_input_id_list()
{
    ID      id, id_list, id_last;
    SYMBOL  sp;
    int     c, i;

    id_last = id_list = NULL;

    for (;;) {	/* infinite loop '[ name storage_class type base ]' */
	c = X_input_skip_space();
	if (c != '[')
	    break;

	/* read 'name' */
	c = X_input_skip_space();
	if (c == '*') {	/* system generated? */
	    sp = NULL;
	}
	else {
	    ungetc(c, input_fp);
	    X_input_name();
	    sp = find_symbol(input_buf);	/* symbol table(on hash) */
	}

	id = new_ident_desc(sp);	/* ident table(not linked) */

	/* read 'storage_class'(enum) of ID */
	c = X_input_skip_space();
	if (c != '*') {
	    ungetc(c, input_fp);
	    X_input_name();
	    for (i = 0 ; i < N_STORAGE_CLASS ; i++)
		if (strcmp(storage_class_names[i], input_buf) == 0)
		    break;
	    if (i == N_STORAGE_CLASS)
		fatal("X_input_id_list: unknown storage class '%s'",
		      input_buf);
	    ID_CLASS(id) = (STORAGE_CLASS) i;
	}

	/* read 'type' */
	c = X_input_skip_space();
	if (c != '*') {
	    ungetc(c, input_fp);
	    ID_TYPE(id) = X_input_type();	/* set type of ID */
	}

	/* read 'base' */
	ID_BASE(id) = X_input_rec();

	/* end check */
	c = X_input_skip_space();
	if (c != ']')
	    fatal("X_input_id_list: bad ident desc");

	/* link new ID to the last of id_list: insert tail! */
	if (id_list == NULL) {
	    id_list = id;
	} else {
	    if (id_last == NULL) {
		id_last = id_list;
	    }
	    ID_NEXT(id_last) = id;
	}
	id_last = id;

	/* if ID is tagname, link it to TYPE_DESC */
	if (ID_CLASS(id) == TAGNAME)
	    TYPE_TAG_NAME(ID_TYPE(id)) = id;
    }

    ungetc(c, input_fp);
    return id_list;
}

#if 0
/* Read first expression next to the '(' */
static expv  X_input(FILE   *fp)
{
    input_fp = fp;
    return X_input_rec();
}
#endif

/* Read expression :: '(name)' */
static expv X_input_rec()
{
    expv    v = NULL;
    enum expr_code code = EXPR_CODE_END;
    TYPE_DESC tp = NULL;
    int     i;
    int     c;

    c = X_input_skip_space();
    if (c == EOF)
	return (NULL);
    if (c != '(')
	fatal("bad expression at '%c'", c);

    c = X_input_skip_space();
    if (c == ')') {		/* '()': Null */
	return NULL;
    }

    ungetc(c, input_fp);
    X_input_name();

	/* search code name */
    for (i = 0; i < MAX_CODE; i++) {
	if (strcmp(expr_code_info[i].code_name, input_buf) == 0)
	    break;
    }
    if (i < MAX_CODE){	/* find code name from the list */
	code = (enum expr_code) i;
    }
    else {
	fatal("X_input_rec: Unknown code name, '%s'", input_buf);
    }

	/* type delimiter check */
    if ((c = getc(input_fp)) == ':') {	/* next string is type info */
	tp = X_input_type();
    }
    else if ( c == '@' ){	/* skip line # and file name */
	char *fname = (char *)xmalloc(1024);
	int llen, lnum = X_input_int();
	lineno_info *li = (lineno_info *)xmalloc(sizeof(lineno_info));
	li->ln_no = lnum;
	c = getc(input_fp);
	if ( c == '.' ){ /* file name */
	    while(1){
		c = getc(input_fp);
		if ( c == '(' )
		    break;
		else if ( c == '/' )
		    strcat(fname, "/");
		else if ( c == '.' )
		    strcat(fname, ".");
		else if ( c == '-' )
		    strcat(fname, "-");
		else{
		    ungetc(c, input_fp);
		    X_input_name();
		    if ( input_buf == NULL )
			break;
		    strcat(fname, input_buf);
		}
	    }
	    llen = strlen(fname);
	    if ( llen > 0 ){
		char * fn = (char *)xmalloc(llen+1);
		strcpy(fn, fname);
/*		li->file_id = (short)fn;??*/
	    }
	}
	ungetc(c, input_fp);
    }
    else {	/* no type */
	ungetc(c, input_fp);
	tp = NULL;
    }

	/* if the code name is terminal... */
    if (EXPR_CODE_IS_TERMINAL(code)) {
	switch (code) {
	case ID_LIST:
	    /* list is transformed, and create expv struct(type is Null) */
	    v = expv_any_term(ID_LIST, (void *)X_input_id_list());
	    break;

	case IDENT:		/* NAME */
	case VAR_ADDR:		/* ICON */
	case LVAR_ADDR:
	case PARAM_ADDR:
	case PARAM_VAR:
	case LVAR:
	case VAR:
	case ARRAY_ADDR:
	case LARRAY_ADDR:
	case FUNC_ADDR:
	case MOE_CONSTANT:
	    X_input_name();
	    v = expv_sym_term(code, tp, find_symbol(input_buf));
	    break;

	case INT_CONSTANT:
	    v = expv_int_term(code, tp, X_input_int());
	    break;

	case LONG_CONSTANT:
	    v = expv_long_term(code, tp, X_input_long());
	    break;

	case STRING_CONSTANT:
	    X_input_str();
	    v = expv_str_term(tp, strdup(input_buf));
	    break;

	case LONGLONG_CONSTANT:
	case FLOAT_CONSTANT:
	    v = expv_int_term(code, tp, 0);
	    EXPV_LLINT_HIGH(v) = X_input_int();
	    EXPV_LLINT_LOW(v) = X_input_int();
	    break;

	default:
	    fatal("X_input_rec: Unknown terminal");
	}

	/* list end, ')', check */
	if (X_input_skip_space() != ')')
	    fatal("')' is expected");

	return (v);
    }

    /* not terminal; input list */
    v = expv_int_term(code, tp, 0);
    while (1) {
	if ((c = X_input_skip_space()) == ')')
	    break;
	ungetc(c, input_fp);
	v = list_put_last(v, X_input_rec());
    }

    return (v);
}

/* skip space charcters, and comment */
static int X_input_skip_space()
{
    register int c;

    while ((c = getc(input_fp)) != 0) {
	switch (c) {
	case ' ':	/* white space */
	case '\t':	/* tab */
	case '\n':	/* newline */
	case '\r':	/* return */
	case '\f':	/* formfeed */
	    break;

	case ';':	/* lisp style comment */
	case '#':	/* pragma */
	    while ((c = getc(input_fp)) != '\n') {	/* skip this line */
		if (c == EOF)
		    goto bad_EOF;
	    }
	    break;

	case '/':		/* C style comment or '/' */
	    if ((c = getc(input_fp)) != '*') {
		ungetc(c, input_fp);
		return ('/');
	    }
	    while ((c = getc(input_fp)) != EOF) {
		if (c == '*' && (c = getc(input_fp)) == '/')
		    goto next;
	    }
	    goto bad_EOF;

	case EOF:	/* end of file */
	default:
	    return (c);
	}
    next:
	;
    }
    return (c);
    
bad_EOF:
    fatal("unexpected EOF");
    return EOF;
}

/* read integer value */
static int X_input_int()
{
    char    c, *cp;
    int     i;

    c = X_input_skip_space();
    if (c == '0') {
	c = getc(input_fp);
	if (c == 'x' || c == 'X')	/* hex */
	    c = getc(input_fp);
	else {
	    ungetc(c, input_fp);
	    return 0;
	}
    }

    if (!isxdigit((int)c))
	fatal("integer is expected");
    cp = input_buf;
    do {
	*cp++ = c;
	c = getc(input_fp);
    } while (isxdigit((int)c));

    ungetc(c, input_fp);
    *cp = 0;

    sscanf(input_buf, "%x", &i);
    return (i);
}

/* read long integer value */
static long X_input_long()
{
    char    c, *cp;
    long     i;

    c = X_input_skip_space();
    if (c == '0') {
	c = getc(input_fp);
	if (c == 'x' || c == 'X')	/* hex */
	    c = getc(input_fp);
	else {
	    ungetc(c, input_fp);
	    return 0;
	}
    }

    if (!isxdigit((int)c))
	fatal("integer is expected");
    cp = input_buf;
    do {
	*cp++ = c;
	c = getc(input_fp);
    } while (isxdigit((int)c));

    ungetc(c, input_fp);
    *cp = 0;

    sscanf(input_buf, "%lx", &i);
    return (i);
}

/* Read symbol name to input_buf */
static void X_input_name()
{
    char   *cp;
    int     c;

    c = X_input_skip_space();
    ungetc(c, input_fp);
    for (cp = input_buf; ((c = getc(input_fp)) == '_' || isalnum(c)) ; cp++) {
	if (cp < &input_buf[XIO_BUF_LEN])
	    *cp = c;
	else
	    fatal("X_input_name: too long name");
    }
    *cp = '\0';
    ungetc(c, input_fp);
}

/* Read string(exept '"') to input_buf */
static void X_input_str()
{
    char    c, *cp;
    int     i;

    c = X_input_skip_space();
    if (c != '"')
	fatal("string expected");

    for (cp = input_buf, i = 0 ; (c = getc(input_fp)) != '"' ; i++) {
	if ( c == '\\' ){	/* escape */
	    *cp++ = c;
	    i++;
	    if ( i > XIO_BUF_LEN - 1 )
		fatal("X_input_str: too long string");
	    c = (char) getc(input_fp);
	}
	if ( i > XIO_BUF_LEN - 1 )
	    fatal("X_input_str: too long string");
	*cp++ = c;
    }
    *cp = 0;
}


/*
 * OUTPUT: transform internal structure to the text readable format
 */

/* Write intermediate structure to the text-form */
void output_X_file()
{
    TYPE_DESC tp;
    list    lp;

    /* output file name */
    fprintf(out_X_file,"### source:%s\n",source_file_name);

    current_line = NULL;

	/* 1st: output type list information */
    for( tp = type_list ; tp != NULL ; tp = TYPE_LINK(tp) ){
	if (TYPE_DESC_CODE(tp) == BASIC_TYPE_NODE &&
	    !TYPE_IS_CONST(tp) && !TYPE_IS_VOLATILE(tp))
	    continue;	/* NO type output for basic C type(int,etc) */

	fprintf(out_X_file, "{");	/* brace as start */

	X_output_type(tp, out_X_file);	/* type (and its ID) */

	fprintf(out_X_file, " 0x%x 0x%x %x %x ", TYPE_SIZE(tp),
		TYPE_ALIGN(tp), TYPE_IS_CONST(tp), TYPE_IS_VOLATILE(tp));

	if(TYPE_IS_CONST(tp) || TYPE_IS_VOLATILE(tp)){
	    /* indirect node for const/volatile qualified type */
	    switch (TYPE_DESC_CODE(tp)) {
	    case BASIC_TYPE_NODE:
		fprintf(out_X_file,"%s",
			basic_data_type_names[(int) TYPE_BASIC_TYPE(tp)]);
		fprintf(out_X_file, "}\n");
		continue;
	    case STRUCT_TYPE:
	    case UNION_TYPE:
	    case ENUM_TYPE:
		X_output_type(ID_TYPE(TYPE_TAG_NAME(tp)),out_X_file);
		fprintf(out_X_file, "}\n");
		continue;
	    default:
		break;
	    } 
	    /* Fall through */
	}

	switch (TYPE_DESC_CODE(tp)) {
	case POINTER_DECL:
	    X_output_type(TYPE_REF(tp), out_X_file);
	    break;
	    
	case ARRAY_DECL:
	    X_output_type(TYPE_REF(tp), out_X_file);
	    fprintf(out_X_file, " 0x%x", TYPE_ARRAY_DIM(tp));
	    break;
	    
	case FUNCTION_DECL:
	    X_output_type(TYPE_REF(tp), out_X_file);
	    fprintf(out_X_file, " %d ", TYPE_IS_FUNC_PROTO(tp));
	    /* expr_print(TYPE_FUNC_PARAMS(tp), out_X_file); */
	    X_output_no_indent(TYPE_FUNC_PARAMS(tp), out_X_file);
	    break;
	    
	case ENUM_TYPE:
	    X_output_no_indent(TYPE_MOE_LIST(tp), out_X_file);
	    break;
	    
	case STRUCT_TYPE:
	case UNION_TYPE:
	case CLASS_TYPE:
	    X_output_id_list(TYPE_MEMBER_LIST(tp), out_X_file);
	    break;
	default:
	    fatal("output_X_file: bad type");
	}
	fprintf(out_X_file, "}\n");
    }

	/* 2nd: output global env(level zero ID list) */
    fprintf(out_X_file, "%%\n");
    X_output_id_list(GLOBAL_ID_LIST, out_X_file);

	/* 3rd: output statement list */
    fprintf(out_X_file, "\n%%\n");
    FOR_ITEMS_IN_LIST(lp, GLOBAL_DECL_LIST)
	X_output(LIST_ITEM(lp), out_X_file);
}

/* type information: name for basic C type or type char and ID(addr) */
static void X_output_type(TYPE_DESC tp, FILE   *fp)
{
    if((TYPE_IS_CONST(tp) || TYPE_IS_VOLATILE(tp)) &&
       (TYPE_DESC_CODE(tp) == BASIC_TYPE_NODE ||
	TYPE_DESC_CODE(tp) == ENUM_TYPE ||
	TYPE_DESC_CODE(tp) == STRUCT_TYPE ||
	TYPE_DESC_CODE(tp) == UNION_TYPE)){
	/* const/volatile qualified node */
#ifdef not /* ADDR_IS_64 */
#ifdef HAS_QUAD_PRINT
	fprintf(fp,"B%qx", (_omAddrInt_t)tp);
#else
	fprintf(fp,"B%lx", (_omAddrInt_t)tp);
#endif /* HAS_QUAD_PRINT */
#else
	fprintf(fp,"B%x", (unsigned int)((_omAddrInt_t)tp));
#endif /* ADDR_IS_64 */
	return;
    }

    switch (TYPE_DESC_CODE(tp)) {
    case BASIC_TYPE_NODE:	/* output typename(using type table) */
	fprintf(fp, "%s",basic_data_type_names[(int) TYPE_BASIC_TYPE(tp)]);
	break;
#ifdef not /* ADDR_IS_64 */
#ifdef HAS_QUAD_PRINT
    case POINTER_DECL:	/* type specified char and struct address as ID */
	fprintf(fp, "P%qx", (_omAddrInt_t) tp);
	break;
    case ARRAY_DECL:
	fprintf(fp, "A%qx", (_omAddrInt_t)tp);
	break;
    case FUNCTION_DECL:
	fprintf(fp, "F%qx", (_omAddrInt_t)tp);
	break;
    case ENUM_TYPE:
	fprintf(fp, "E%qx", (_omAddrInt_t)tp);
	break;
    case STRUCT_TYPE:
	fprintf(fp, "S%qx", (_omAddrInt_t)tp);
	break;
    case UNION_TYPE:
	fprintf(fp, "U%qx", (_omAddrInt_t)tp);
	break;
    case CLASS_TYPE:
	fprintf(fp, "C%qx", (_omAddrInt_t)tp);
	break;
#else
    case POINTER_DECL:	/* type specified char and struct address as ID */
	fprintf(fp, "P%lx", (_omAddrInt_t) tp);
	break;
    case ARRAY_DECL:
	fprintf(fp, "A%lx", (_omAddrInt_t)tp);
	break;
    case FUNCTION_DECL:
	fprintf(fp, "F%lx", (_omAddrInt_t)tp);
	break;
    case ENUM_TYPE:
	fprintf(fp, "E%lx", (_omAddrInt_t)tp);
	break;
    case STRUCT_TYPE:
	fprintf(fp, "S%lx", (_omAddrInt_t)tp);
	break;
    case UNION_TYPE:
	fprintf(fp, "U%lx", (_omAddrInt_t)tp);
	break;
    case CLASS_TYPE:
	fprintf(fp, "C%lx", (_omAddrInt_t)tp);
	break;
#endif /* HAS_QUAD_PRINT */
#else
    case POINTER_DECL:	/* type specified char and struct address as ID */
	fprintf(fp, "P%x", (unsigned int)((_omAddrInt_t)tp));
	break;
    case ARRAY_DECL:
	fprintf(fp, "A%x", (unsigned int)((_omAddrInt_t)tp));
	break;
    case FUNCTION_DECL:
	fprintf(fp, "F%x", (unsigned int)((_omAddrInt_t)tp));
	break;
    case ENUM_TYPE:
	fprintf(fp, "E%x", (unsigned int)((_omAddrInt_t)tp));
	break;
    case STRUCT_TYPE:
	fprintf(fp, "S%x", (unsigned int)((_omAddrInt_t)tp));
	break;
    case UNION_TYPE:
	fprintf(fp, "U%x", (unsigned int)((_omAddrInt_t)tp));
	break;
    case CLASS_TYPE:
	fprintf(fp, "C%x", (unsigned int)((_omAddrInt_t)tp));
	break;
#endif /* ADDR_IS_64 */
    default:	/* the other types are not expected */
	fatal("X_output: bad type");
    }
}

static FILE *print_fp;
static void  X_output_rec();

/* write decl and func */
void X_output(expv v, FILE *fp)
{
    print_fp = fp;
    X_output_rec(v, 0);
    fprintf(print_fp, "\n");
}

/* write decl and func with level specifier */
void X_output2(expv v, FILE   *fp, int  l /* output level */)
{
    print_fp = fp;
    X_output_rec(v, l);
    fprintf(print_fp, "\n");
}

/* write indent level -1 */
static void X_output_no_indent(expv v, FILE   *fp)
{
    print_fp = fp;
    X_output_rec(v, -1);
}

/* write general expression for specific (indent) level */
static void X_output_rec(expv    v, int     l	/* indent level */)
{
    int     i;
    char   *expv_type_name();
    struct list_node *lp;
    TYPE_DESC tp;

    for (i = 0; i < l; i++)    /* indent = l times 2 */
	fprintf(print_fp, "  ");

    if (v == NULL) {	/* special case: Null expr */
	fprintf(print_fp, "()");
	return;
    }

    /* XCODE NAME */
#ifdef SCANNER_READ_L_AS_LL
    if (EXPV_CODE(v) != LONG_CONSTANT) {
	fprintf(print_fp, "(%s", EXPR_CODE_NAME(EXPV_CODE(v)));
	
	if ((tp = EXPV_TYPE(v)) != NULL) {
	    fprintf(print_fp, ":");
	    X_output_type(tp, print_fp);
	}
    }
#else
    fprintf(print_fp, "(%s", EXPR_CODE_NAME(EXPV_CODE(v)));

    if ((tp = EXPV_TYPE(v)) != NULL) {
	fprintf(print_fp, ":");
	X_output_type(tp, print_fp);
    }
#endif /* SCANNER_READ_L_AS_LL */

    if (EXPR_CODE_IS_TERMINAL(EXPV_CODE(v))) {
	switch (EXPV_CODE(v)) {
	case ID_LIST:
	    X_output_id_list(EXPV_ANY(ID, v), print_fp);
	    fprintf(print_fp, ")");
	    return;
	case IDENT:		/* NAME */
	case VAR_ADDR:		/* ICON */
	case LVAR_ADDR:
	case PARAM_ADDR:
	case PARAM_VAR:
	case LVAR:
	case VAR:
	case ARRAY_ADDR:
	case LARRAY_ADDR:
	case FUNC_ADDR:
	case MOE_CONSTANT:
	    fprintf(print_fp, " %s)", SYM_NAME(EXPV_NAME(v)));
	    return;
	case CXX_ATTR_DECL:
	case INT_CONSTANT:
	    fprintf(print_fp, " %x)", EXPV_INT_VALUE(v));
	    return;	    
	case LONG_CONSTANT:
#ifdef SCANNER_READ_L_AS_LL
	    {
		unsigned long lVal = EXPV_ULINT_VALUE(v);
		if (lVal > UINT_MAX) {
		    fprintf(print_fp, "(%s:", EXPR_CODE_NAME(LONGLONG_CONSTANT));
		    X_output_type(longlong_type, print_fp);
		    fprintf(print_fp, " %x %x)", EXPV_LLINT_HIGH(v), EXPV_LLINT_LOW(v));
		} else {
		    fprintf(print_fp, "(%s:", EXPR_CODE_NAME(LONG_CONSTANT));
		    X_output_type(long_type, print_fp);
		    fprintf(print_fp, " %x)", EXPV_ULINT_VALUE(v));
		}
		return;
	    }
#else
	    fprintf(print_fp, " %x)", EXPV_INT_VALUE(v));
	    return;
#endif /* SCANNER_READ_L_AS_LL */

	case STRING_CONSTANT:
	    fprintf(print_fp, " ");
	    X_output_str_constant(EXPV_STR(v),print_fp);
	    fprintf(print_fp, ")");
	    return;
	case LONGLONG_CONSTANT:
	case FLOAT_CONSTANT:
	    fprintf(print_fp, " %x %x)", EXPV_LLINT_HIGH(v), EXPV_LLINT_LOW(v));
	    return;
	case ERROR_NODE:
	    fprintf(print_fp, ")");
	default:
	    fatal("X_output: unknown terminal");
	}
    }

    /* print lineno */
    if(l >= 0 && EXPR_LINE(v) != NULL){
	if(current_line == NULL ||
	   EXPR_LINE_FILE_ID(v) != current_line->file_id){
	    fprintf(print_fp,"@%d.%s",EXPR_LINE_NO(v),
		    FILE_NAME(EXPR_LINE_FILE_ID(v)));
	} else {
	    fprintf(print_fp,"@%d",EXPR_LINE_NO(v));
	}
	current_line = EXPR_LINE(v);
    }

    if (EXPV_LIST(v) != NULL) {
	if (l < 0)
	    fprintf(print_fp, " ");
	else {
	    fprintf(print_fp, "\n");
	    l++;
	}
	for (lp = EXPV_LIST(v); lp != NULL; lp = lp->l_next) {
	    X_output_rec(lp->l_item, l);
	    if (lp->l_next != NULL) {
		if (l < 0)
		    fprintf(print_fp, " ");
		else
		    fprintf(print_fp, "\n");
	    }
	}
    }
    fprintf(print_fp, ")");
}

static void X_output_str_constant(char *s,FILE *fp)
{
    char c;
    fprintf(fp,"\"");
    while((c = *s++) != '\0'){
	if(c == '\"' || c == '\\' || (c < 0x20) || (c >= 0x7F))
	    fprintf(fp,"\\%03o",c);
	else
	    fprintf(fp,"%c",c);
    }
    fprintf(fp,"\"");
}

/* 2nd part: output ID table list */
static void X_output_id_list(ID id_list, FILE *fp)
{
    ID      id;

    for( id = id_list ; id != NULL ; id = ID_NEXT(id) ){
	if (ID_TYPE(id) != NULL && !ID_TYPE(id)->is_referenced)
	    continue;

	fprintf(fp, " [");

	if (ID_SYM(id))
	    fprintf(fp, "%s ", ID_NAME(id));
	else
	    fprintf(fp, "* ");

	fprintf(fp, "%s ", storage_class_name(ID_CLASS(id)));

	if (ID_TYPE(id))
	    X_output_type(ID_TYPE(id), fp);
	else
	    fprintf(fp, "*");
	fprintf(fp, " ");

	X_output_no_indent(ID_BASE(id), fp);

	if(ID_BIT_FIELD_LEN(id) != 0)
	    fprintf(fp," %d",ID_BIT_FIELD_LEN(id));

	fprintf(fp, " ]\n");
    }
}

/**** some utility function ****/
/* basic type name query */
char   * basic_type_name(BASIC_DATA_TYPE type)
{
    return (basic_data_type_names[(int) type]);
}

/* storage class name query */
char   * storage_class_name(STORAGE_CLASS class)
{
    return (storage_class_names[(int) class]);
}

/* storage qualifier name query */
static char * type_qual_name(TYPE_QUAL qual)
{
    return (type_qualifier_names[(int) qual]);
}
/**** some utility function end ****/

/* collect type information and make type list */
void collect_types()
{
    list    lp;

    type_list = NULL;
    type_list_tail = NULL;
	/* collect used types */
    mark_type_desc_in_id_list(GLOBAL_ID_LIST);

    FOR_ITEMS_IN_LIST(lp, GLOBAL_DECL_LIST)
	collect_type_desc(LIST_ITEM(lp));
}

/* walk through expr for ref-ed check */
static void collect_type_desc(expv v)
{
    list    lp;

    if (v == NULL)
	return;
    if ( debug_flag2 && NULL ){
	fprintf(debug_fp, "<========>\n");
	debug_output_exp(v);
    }
    mark_type_desc(EXPV_TYPE(v));
    if ( debug_flag2 && NULL )
	fprintf(debug_fp, "<========>\n");

    if (EXPR_CODE_IS_TERMINAL(EXPV_CODE(v))) {
	if (EXPR_CODE(v) == ID_LIST) {
	    mark_type_desc_in_id_list(EXPV_ANY(ID, v));
	}
	return;
    }

    for (lp = EXPV_LIST(v) ; lp != NULL ; lp = lp->l_next) {
	collect_type_desc(lp->l_item);
    }
}

/* mark type table recursively as ref-ed */
static void mark_type_desc(TYPE_DESC tp)
{
    if (tp == NULL)
	return;

    if (tp->is_referenced)	/* already marked */
	return;

    tp->is_referenced = 1;	/* mark this type as ref-ed */
    switch (TYPE_DESC_CODE(tp)) {
    case BASIC_TYPE_NODE:
	break;

    case POINTER_DECL:
    case ARRAY_DECL:
	mark_type_desc(TYPE_REF(tp));
	break;

    case FUNCTION_DECL:
	mark_type_desc(TYPE_REF(tp));
	collect_type_desc(TYPE_FUNC_PARAMS(tp));
	break;

    case ENUM_TYPE:
	if(TYPE_TAG_NAME(tp) != NULL) 
	    mark_type_desc(ID_TYPE(TYPE_TAG_NAME(tp))); 
	break;

    case STRUCT_TYPE:
    case UNION_TYPE:
    case CLASS_TYPE:
	mark_type_desc_in_id_list(TYPE_MEMBER_LIST(tp));
	if(TYPE_TAG_NAME(tp) != NULL) 
	    mark_type_desc(ID_TYPE(TYPE_TAG_NAME(tp)));
	break;

    case ERROR_NODE:
	return; /* error recovery */
    default:
	fatal("mark_type_desc: bad type");
    }

	/* id_lists[] are in-order(probably) */
    if ( type_list == NULL )
	type_list = tp;
    else
	TYPE_LINK(type_list_tail) = tp;
    type_list_tail = tp;

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

/* search ID list and mark used type */
static void mark_type_desc_in_id_list(ID idp)
{
    ID      idq;

    for (idq = idp ; idq != NULL ; idq = ID_NEXT(idq)) {
	switch (ID_CLASS(idq)) {
	case LABEL:
	case ULABEL:
	case TAGNAME:
	case MOE:
	case TYPEDEF_NAME:
	    break;

	default:
	    if ( debug_flag2 && NULL )
		fprintf(debug_fp, "[ID mark...]\n");
	    mark_type_desc(ID_TYPE(idq));
	    collect_type_desc(ID_BASE(idq));
	}
    }
}

/* collect type information and make type list */
void debug_collect_type_dummy(expv ex)
{
    list    lp;

    type_list = NULL;

    FOR_ITEMS_IN_LIST(lp, ex)
	collect_type_desc(LIST_ITEM(lp));

    type_list = NULL;
}

/*
 * FOR DEBUG:
 */
void expr_print_msg(char *msg,expr x, FILE *fp)
{
    fprintf(fp,"%s:",msg);
    expr_print(x,fp);
}

/* expression print */
void expr_print(expr    x, FILE   *fp)
{
    print_fp = fp;
    expr_print_rec(x, 0);
    fprintf(print_fp, "\n");
}

/* tree print routine */
static void expr_print_rec(expr x, int l)
{
    int     i;
    struct list_node *lp;

    for (i = 0 ; i < l ; i++)	/* indent */
	fprintf(print_fp, "    ");

    if (x == NULL) {	/* special case */
	fprintf(print_fp, "<NULL>");
	return;
    }

    fprintf(print_fp, "(%s", EXPR_CODE_NAME(EXPR_CODE(x)));

    if(EXPR_LINE(x) != NULL)
      fprintf(print_fp, ":%d", EXPR_LINE_NO(x));

    switch (EXPR_CODE(x)) {
    case ID_LIST:
	print_id_list(NULL,EXPV_ANY(ID, x), print_fp);
	fprintf(debug_fp, ")");
	return;

    case IDENT:
    case TYPENAME_IDENT:
    case VAR_ADDR:		/* ICON */
    case LVAR_ADDR:
    case PARAM_ADDR:
    case PARAM_VAR:
    case LVAR:
    case VAR:
    case ARRAY_ADDR:
    case LARRAY_ADDR:
    case FUNC_ADDR:
    case MOE_CONSTANT:
	fprintf(print_fp, " \"%s\")", SYM_NAME(EXPR_SYM(x)));
	break;

    case STRING_CONSTANT:
	fprintf(print_fp, " ");
	X_output_str_constant(EXPV_STR(x),print_fp);
	fprintf(print_fp, ")");
	return;

    case CXX_ATTR_DECL:
    case INT_CONSTANT:
	fprintf(print_fp, " %d)", EXPR_INT(x));
	return;

    case LONG_CONSTANT:
	fprintf(print_fp, " %ld)", EXPR_LINT(x));
	return;

    case FLOAT_CONSTANT:
	fprintf(print_fp, " %g)", EXPR_FLOAT(x));
	return;

    case LONGLONG_CONSTANT:
	fprintf(print_fp, " 0x%x 0x%x)",
		EXPR_LLINT_HIGH(x), EXPR_LLINT_LOW(x));
	return;

    case BASIC_TYPE_NODE:
	fprintf(print_fp, " <%s>)", basic_type_name(EXPR_TYPE(x)));
	return;

    case STORAGE_CLASS_NODE:
	fprintf(print_fp, " <%s>)", storage_class_name(EXPR_SCLASS(x)));
	return;

    case TYPE_QUAL_NODE:
	fprintf(print_fp, " <%s>)", type_qual_name(EXPR_TYPE_QUAL(x)));
	return;

    default:	/* list */
	if ((lp = EXPR_LIST(x)) == NULL) {
	    fprintf(print_fp, ")");
	    return;
	}
	for ( ; lp != NULL; lp = LIST_NEXT(lp)) {
	    fprintf(print_fp, "\n");
	    expr_print_rec(LIST_ITEM(lp), l + 1);
	}
	fprintf(print_fp, ")");
	break;
    }
}

/* type output, the same as IR output */
void debug_output_X_type()
{
    TYPE_DESC tp;

    for (tp = type_list ; tp != NULL ; tp = TYPE_LINK(tp)) {
	if (TYPE_DESC_CODE(tp) == BASIC_TYPE_NODE)
	    continue;	/* NO type output for basic C type(int,etc) */

	fprintf(debug_fp, "{");	/* brace as start */

	X_output_type(tp, debug_fp);	/* type (and its ID) */

	fprintf(debug_fp, " 0x%x 0x%x %x %x ", TYPE_SIZE(tp),
		TYPE_ALIGN(tp), TYPE_IS_CONST(tp), TYPE_IS_VOLATILE(tp));

	switch (TYPE_DESC_CODE(tp)) {
	case POINTER_DECL:
	    X_output_type(TYPE_REF(tp), debug_fp);
	    break;

	case ARRAY_DECL:
	    X_output_type(TYPE_REF(tp), debug_fp);
	    fprintf(debug_fp, " 0x%x", TYPE_ARRAY_DIM(tp));
	    break;

	case FUNCTION_DECL:
	    X_output_type(TYPE_REF(tp), debug_fp);
	    fprintf(debug_fp, " %d ", TYPE_IS_FUNC_PROTO(tp));
	    X_output_no_indent(TYPE_FUNC_PARAMS(tp), debug_fp);
	    break;

	case ENUM_TYPE:
	    X_output_no_indent(TYPE_MOE_LIST(tp), debug_fp);
	    break;

	case STRUCT_TYPE:
	case UNION_TYPE:
	    X_output_id_list(TYPE_MEMBER_LIST(tp), debug_fp);
	    break;

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

	fprintf(debug_fp, "}\n");
    }
}

/* expr code output for debug */
static void debug_output_exp(expv  v)
{
    char   *expv_type_name();
/*    
    int     i;
    struct list_node *lp;
    TYPE_DESC tp;
    */

    if (v == NULL) {	/* special case: Null expr */
	fprintf(debug_fp, "(--NULL--)\n");
	return;
    }

	/* XCODE NAME */
    fprintf(debug_fp, "(%s :", EXPR_CODE_NAME(EXPV_CODE(v)));

/*    if (tp = EXPV_TYPE(v)) {
	X_output_type(tp, debug_fp);
    }*/

    if (EXPR_CODE_IS_TERMINAL(EXPV_CODE(v))) {
	switch (EXPV_CODE(v)) {
	case ID_LIST:
	    X_output_id_list(EXPV_ANY(ID, v), debug_fp);
	    fprintf(debug_fp, ")\n");
	    return;

	case IDENT:		/* NAME */
	case VAR_ADDR:		/* ICON */
	case LVAR_ADDR:
	case PARAM_ADDR:
	case PARAM_VAR:
	case LVAR:
	case VAR:
	case ARRAY_ADDR:
	case LARRAY_ADDR:
	case FUNC_ADDR:
	case MOE_CONSTANT:
	    fprintf(debug_fp, " %s)\n", SYM_NAME(EXPV_NAME(v)));
	    return;

	case INT_CONSTANT:
	    fprintf(debug_fp, " %x)\n", EXPV_INT_VALUE(v));
	    return;

	case LONG_CONSTANT:
	    fprintf(debug_fp, " %lx)\n", EXPV_LINT_VALUE(v));
	    return;

	case STRING_CONSTANT:
	    fprintf(print_fp, " ");
	    X_output_str_constant(EXPV_STR(v),print_fp);
	    fprintf(print_fp, ")");
	    return;

	case LONGLONG_CONSTANT:
	case FLOAT_CONSTANT:
	    fprintf(debug_fp, " %x %x)\n", EXPV_LLINT_HIGH(v), EXPV_LLINT_LOW(v));
	    return;

	case ERROR_NODE:
	    fprintf(debug_fp, ")\n");

	default:
	    break;
	}
	fatal("debug-out-expr:: unknown terminal\n");
    }
}


