static char rcsid[] = "$Id: X-decompile.c,v 1.1 2002/02/06 16:05:00 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.
 *  
 *  
 *  $
 */
/* C source code output */

#include "C-front.h"

#define NL    fprintf(output_file,"\n")

static void 	decompile_decl_id_list _ANSI_ARGS_((ID id_list));
static void 	decompile_decl _ANSI_ARGS_((expv v));
static ID 	lookup_id_env _ANSI_ARGS_((expv name));
static ID 	lookup_env _ANSI_ARGS_((expv name));
static void 	decompile_declaration _ANSI_ARGS_((ID id));
static void 	decompile_type_name _ANSI_ARGS_((TYPE_DESC tq));
static void 	decompile_decl_type _ANSI_ARGS_((TYPE_DESC tp, char *name));
static void 	decompile_declarator_rec _ANSI_ARGS_((TYPE_DESC *decls, int i, char *name));
static void 	decompile_initializer _ANSI_ARGS_((expv init));
static void 	decompile_statement _ANSI_ARGS_((expv v));
static void 	decompile_body _ANSI_ARGS_((expv v));
static void 	decompile_expression _ANSI_ARGS_((expv v));

/* decompiled C-source code output */
void decompile_file()
{
    list    lp;

    fprintf(output_file, "/* decompiled C source code by OpenMP C Compiler v0.1 */\n\n");
	/* structure/union/enum declarations with no storage class */
    decompile_decl_id_list(GLOBAL_ID_LIST);

	/* global declarations and functions with storage class */
    FOR_ITEMS_IN_LIST(lp, GLOBAL_DECL_LIST){
	decompile_decl(LIST_ITEM(lp));
    }
}

/*
 * globaly declared structure without storage declarations
 * --there is no nested declaration
 */
static void decompile_decl_id_list(ID id_list)
{
    TYPE_DESC tp;
    list    lp;
    ID      id, idp;
    int  i;

    /* scan for tag name:: output struct/union type name only */
    for (idp = id_list; idp != NULL; idp = ID_NEXT(idp)) {
	if (ID_CLASS(idp) != TAGNAME)
	    continue;
	tp = ID_TYPE(idp);
	if (!tp->is_referenced)
	    continue;

	switch (TYPE_DESC_CODE(tp)) {
	case STRUCT_TYPE:
	case UNION_TYPE:
	    decompile_type_name(tp);
	    fprintf(output_file, ";\n");
	    break;
	default:
	    break;
	}
    }

    /* declaration for body */
    for( idp = id_list, i = 0 ; idp != NULL ; idp = ID_NEXT(idp), i++ ){
	if ( debug_X_flag2 ){
	    fprintf(debug_fp, "ID table[%d]-----------\n", i);
	    print_ID(idp, debug_fp);
	    fprintf(debug_fp, "--------------------------------\n");
	}
	if (ID_CLASS(idp) != TAGNAME)
	    continue;

	tp = ID_TYPE(idp);
	if (!tp->is_referenced)
	    continue;

	switch (TYPE_DESC_CODE(tp)) {
	case STRUCT_TYPE:
	case UNION_TYPE:
	    decompile_type_name(tp);
	    fprintf(output_file, " {\n");
	    for (id = TYPE_MEMBER_LIST(tp); id != NULL; id = ID_NEXT(id)) {
		fprintf(output_file, "    ");
		decompile_declaration(id);
		fprintf(output_file, ";\n");
	    }
	    fprintf(output_file, "};\n");
	    break;

	case ENUM_TYPE:
	    decompile_type_name(tp);
	    fprintf(output_file, " {\n");
	    FOR_ITEMS_IN_LIST(lp, TYPE_MOE_LIST(tp)) {
		id = lookup_env(LIST_ITEM(lp));
		if (ID_CLASS(id) != MOE)
		    fatal("decompile_types: bad MOE");
		fprintf(output_file, "    %s", ID_NAME(id));
		if (ID_BASE(id)) {
		    fprintf(output_file, "=");
		    decompile_expression(ID_BASE(id));
		}
		fprintf(output_file, ",\n");
	    }
	    fprintf(output_file, "};\n");
	    break;

	default:
	    ;
	}
    }

/*    fprintf(output_file, "/ * --------- * /\n");*/
}

/* output declarations with storage class and functions */
static void decompile_decl(expv v)
{
    list    lp;
    TYPE_DESC tp;
    ID      id;

    switch(EXPV_CODE(v)){
    case LIST:
	FOR_ITEMS_IN_LIST(lp, v) {
	    v = LIST_ITEM(lp);
	    decompile_decl(v);
	}
	break;
    case FUNCTION_DEFINITION:	/* functions */
	/* (FUNCTION_DEFINITION id param-id-list body) */
	fprintf(output_file, "\n");
	id = lookup_env(EXPR_ARG1(v));
	tp = ID_TYPE(id);
	if (TYPE_DESC_CODE(tp) != FUNCTION_DECL)
	    fatal("compile_decl: bad function type");
	if (ID_CLASS(id) == STATIC)
	    fprintf(output_file, "static ");
	decompile_decl_type(TYPE_REF(tp), ID_NAME(id));

	/* print parameters */
	fprintf(output_file, "(");
	FOR_ITEMS_IN_LIST(lp, TYPE_FUNC_PARAMS(tp)) {
	    fprintf(output_file, "%s", SYM_NAME(EXPR_SYM(LIST_ITEM(lp))));
	    if (LIST_NEXT(lp))
		fprintf(output_file, ",");
	}
	fprintf(output_file, ")\n");

	/* print paramter decls */
	id_lists[++current_level] = EXPV_ANY(ID, EXPR_ARG2(v));
	decompile_decl(EXPR_ARG3(v));

	/* print body */
	decompile_statement(EXPR_ARG4(v));
	--current_level;
	break;
    case EXT_DECL:
	fprintf(output_file, "extern ");
    case VAR_DECL:
	id = lookup_id_env(EXPR_ARG1(v));
	decompile_declaration(id);
	if (EXPR_ARG2(v)) {
	    fprintf(output_file, "=");
	    decompile_initializer(EXPR_ARG2(v));
	}
	fprintf(output_file, ";\n");
	/* NL; */
	break;
    default:
	fatal("decompile_decl: unknown code");
    }
}

static ID      lookup_id_env(expv    name)
{
    SYMBOL  sp;
    ID      id;
    int     lev;

    sp = EXPR_SYM(name);
    for (lev = current_level; lev >= 0; lev--)
	for (id = id_lists[lev]; id != NULL; id = ID_NEXT(id))
	    if (ID_SYM(id) == sp && ID_CLASS(id) != TAGNAME )
		return (id);
    fatal("lookup_id_env: '%s' not found",SYM_NAME(sp));
    return NULL;
}

static ID      lookup_env(expv    name)
{
    SYMBOL  sp;
    ID      id;
    int     lev;

    sp = EXPR_SYM(name);
    for (lev = current_level; lev >= 0; lev--)
	for (id = id_lists[lev]; id != NULL; id = ID_NEXT(id))
	    if (ID_SYM(id) == sp)
		return (id);
    fatal("lookup_env: '%s' not found",SYM_NAME(sp));
    return NULL;
}

/* output storage class and pass to the type output func */
static void decompile_declaration(ID id)
{
    switch (ID_CLASS(id)) {
    case AUTO:
	fprintf(output_file, "auto ");
	break;
    case PARAM:
    case EXTDEF:
    case EXTERN:
    case SNULL:
	break;
    case STATIC:
	fprintf(output_file, "static ");
	break;
    case REGISTER:
	fprintf(output_file, "register ");
	break;
    default:
	fatal("decompile_declaration: bad class");
    }
    decompile_decl_type(ID_TYPE(id), ID_NAME(id));
}

/* output type name: if there is no class tag name, make new name by using
 * type head char and table address for struct/union/enum */
static void decompile_type_name(TYPE_DESC tq)
{
    switch (TYPE_DESC_CODE(tq)) {
    case BASIC_TYPE_NODE:	/* predefined basic type */
	switch (TYPE_BASIC_TYPE(tq)) {
	case VOID:
	    fprintf(output_file, "void");
	    break;
	case CHAR:
	    fprintf(output_file, "char");
	    break;
	case SHORT:
	    fprintf(output_file, "short");
	    break;
	case LONG:
	    fprintf(output_file, "long");
	    break;
	case LONGLONG:
	    fprintf(output_file, "long long");
	    break;
	case UNSIGNED_CHAR:
	    fprintf(output_file, "unsigned char");
	    break;
	case UNSIGNED_SHORT:
	    fprintf(output_file, "unsigned short");
	    break;
	case UNSIGNED_LONG:
	    fprintf(output_file, "unsigned long");
	    break;
	case UNSIGNED_LONGLONG:
	    fprintf(output_file, "unsigned long long");
	    break;
	case FLOAT:
	    fprintf(output_file, "float");
	    break;
	case DOUBLE:
	    fprintf(output_file, "double");
	    break;
	case UNSIGNED:
	    fprintf(output_file, "unsigned");
	    break;
	case INT:
	    fprintf(output_file, "int");
	    break;
	default:
	    fatal("decompile_type_name: bad basic type");
	}
	break;
    case STRUCT_TYPE:
	if (ID_SYM(TYPE_TAG_NAME(tq)))
	    fprintf(output_file, "struct %s", ID_NAME(TYPE_TAG_NAME(tq)));
	else
#ifdef ADDR_IS_64
#ifdef HAS_QUAD_PRINT
	    fprintf(output_file, "struct _S%qx", (_omAddrInt_t)tq);
#else
	    fprintf(output_file, "struct _S%lx", (_omAddrInt_t)tq);
#endif /* HAS_QUAD_PRINT */
#else
	    fprintf(output_file, "struct _S%x", (_omAddrInt_t)tq);
#endif /* ADDR_IS_64 */
	break;
    case UNION_TYPE:
	if (ID_SYM(TYPE_TAG_NAME(tq)))
	    fprintf(output_file, "union %s", ID_NAME(TYPE_TAG_NAME(tq)));
	else
#ifdef ADDR_IS_64
#ifdef HAS_QUAD_PRINT
	    fprintf(output_file, "union _U%qx", (_omAddrInt_t)tq);
#else
	    fprintf(output_file, "union _U%lx", (_omAddrInt_t)tq);
#endif /* HAS_QUAD_PRINT */
#else
	    fprintf(output_file, "union _U%x", (_omAddrInt_t)tq);
#endif /* ADDR_IS_64 */
	break;
    case ENUM_TYPE:
	if (ID_SYM(TYPE_TAG_NAME(tq)))
	    fprintf(output_file, "enum %s", ID_NAME(TYPE_TAG_NAME(tq)));
	else
#ifdef ADDR_IS_64
#ifdef HAS_QUAD_PRINT
	    fprintf(output_file, "enum _E%qx", (_omAddrInt_t)tq);
#else
	    fprintf(output_file, "enum _E%lx", (_omAddrInt_t)tq);
#endif /* HAS_QUAD_PRINT */
#else
	    fprintf(output_file, "enum _E%x", (_omAddrInt_t)tq);
#endif /* ADDR_IS_64 */
	break;
    default:
	break;
    }
}

#define MAX_DECL_NEST 30	/* max nest of func/pointer/array */

/* developed each quantifiers */
static void decompile_decl_type(TYPE_DESC tp, char   *name)
{
    TYPE_DESC tq;
    int     nest;
    TYPE_DESC nested_decls[MAX_DECL_NEST];

    nest = 0;
    for (tq = tp ; tq != NULL ; tq = TYPE_REF(tq)) {
	if (TYPE_DESC_CODE(tq) == BASIC_TYPE_NODE ||
	    TYPE_DESC_CODE(tq) == STRUCT_TYPE ||
	    TYPE_DESC_CODE(tq) == UNION_TYPE ||
	    TYPE_DESC_CODE(tq) == ENUM_TYPE)
	    break;
	nested_decls[nest++] = tq;
	if (nest >= MAX_DECL_NEST)
	    fatal("too nested declarators");
    }
    if (tq == NULL)
	fatal("decompile_decl_type: NULL type desc");

    decompile_type_name(tq);
    fprintf(output_file, " ");
    decompile_declarator_rec(nested_decls, nest, name);
}

/* ouput developed quantifiers and id name */
static void decompile_declarator_rec(TYPE_DESC *decls,
			      int i, /* # of decls: max is defined before */
			      char   *name)
{
    TYPE_DESC tp;
    int     pred = 0;

    if (i == 0) {	/* no quantifiers */
	if (name)
	    fprintf(output_file, "%s", name);
	return;
    }

    tp = decls[--i];	/* from the last one */
	/* for bracket placement */
    if (i > 0 && !IS_POINTER(tp) && IS_POINTER(decls[i - 1]))
	pred++;

    switch (TYPE_DESC_CODE(tp)) {
    case POINTER_DECL:
	fprintf(output_file, "*");
	decompile_declarator_rec(decls, i, name);
	break;
    case FUNCTION_DECL:
	if (pred)
	    fprintf(output_file, "(");
	decompile_declarator_rec(decls, i, name);
	if (pred)
	    fprintf(output_file, ")");
	fprintf(output_file, "()");
	break;
    case ARRAY_DECL:		/* array type */
	if (pred)
	    fprintf(output_file, "(");
	decompile_declarator_rec(decls, i, name);
	if (pred)
	    fprintf(output_file, ")");
	if (TYPE_ARRAY_DIM(tp) != 0)
	    fprintf(output_file, "[%d]", TYPE_ARRAY_DIM(tp));
	else
	    fprintf(output_file, "[]");
	break;
    default:
	fatal("decompile_declarator_rec");
    }
}

/* output initialized expression? */
static void decompile_initializer(expv    init)
{
    list    lp;
    if (EXPR_CODE(init) == LIST) {
	fprintf(output_file, "{");
	FOR_ITEMS_IN_LIST(lp, init) {	/* for each expr list of next */
	    decompile_initializer(LIST_ITEM(lp));
	    fprintf(output_file, ",");
	}
	fprintf(output_file, "}");
    }
    else
	decompile_expression(init);
}

/* output each statement expression */
static void decompile_statement(expv    v)
{
    list    lp;
    ID      id;

    if (v == NULL) {		/* for NULL statement */
	fprintf(output_file, ";");
	return;
    }
    switch (EXPV_CODE(v)) {
    default:
	fatal("decompile_statement: unknown code");

    case COMPOUND_STATEMENT:
		/* (COMPOUND_STATEMENT id-list decl statement-list) */
	id_lists[++current_level] = EXPV_ANY(ID, EXPR_ARG1(v));
	fprintf(output_file, "{\n");
	decompile_decl_id_list(id_lists[current_level]);
	decompile_decl(EXPR_ARG2(v));
	decompile_statement(EXPR_ARG3(v));
	fprintf(output_file, "}");
	--current_level;
	break;

    case OMP_PRAGMA:
	warning("OMP_PRAGMA is ignored");
	decompile_statement(EXPR_ARG3(v));
	break;

    case LIST:		/* (LIST statement ....) */
	FOR_ITEMS_IN_LIST(lp, v)
	    decompile_statement(LIST_ITEM(lp));
	return;

    case IF_STATEMENT:	/* (IF_STATMENT cond then-part else-part) */
	fprintf(output_file, "if(");
	decompile_expression(EXPR_ARG1(v));	/* condition */
	fprintf(output_file, ")");
	decompile_body(EXPR_ARG2(v));	/* then part */
	if (EXPR_ARG3(v)) {	/* else part */
	    fprintf(output_file, " else ");
	    decompile_body(EXPR_ARG3(v));
	}
	break;

    case WHILE_STATEMENT:	/* (WHILE_STATEMENT cond body) */
	fprintf(output_file, "while(");
	decompile_expression(EXPR_ARG1(v));
	fprintf(output_file, ")");
	decompile_body(EXPR_ARG2(v));	/* loop body */
	break;

    case FOR_STATEMENT:	/* (FOR init cond iter body) */
	fprintf(output_file, "for(");
	decompile_expression(EXPR_ARG1(v));	/* init */
	fprintf(output_file, ";");
	decompile_expression(EXPR_ARG2(v));	/* cond */
	fprintf(output_file, ";");
	decompile_expression(EXPR_ARG3(v));	/* iter */
	fprintf(output_file, ")\n");
	decompile_body(EXPR_ARG4(v));	/* body */
	break;

    case DO_STATEMENT:		/* (DO_STATEMENT body cond) */
	fprintf(output_file, "do ");
	decompile_body(EXPR_ARG1(v));
	fprintf(output_file, " while(");
	decompile_expression(EXPR_ARG2(v));
	fprintf(output_file, ");");
	break;

    case BREAK_STATEMENT:	/* (BREAK_STATEMENT) */
	fprintf(output_file, "break; ");
	break;

    case CONTINUE_STATEMENT:	/* (CONTINUE_STATEMENT) */
	fprintf(output_file, "continue; ");
	break;

    case GOTO_STATEMENT:	/* (GOTO_STATEMENT label) */
	id = lookup_env(EXPR_ARG1(v));
	fprintf(output_file, "goto %s;", ID_NAME(id));
	break;

    case STATEMENT_LABEL:	/* (STATEMENT_LABEL label_ident) */
	id = lookup_env(EXPR_ARG1(v));
	fprintf(output_file, "%s:", ID_NAME(id));
	break;

    case SWITCH_STATEMENT:	/* (SWITCH_STATEMENT value body) */
	fprintf(output_file, "switch(");
	decompile_expression(EXPR_ARG1(v));
	fprintf(output_file, ")\n");
	decompile_body(EXPR_ARG2(v));	/* case body */
	break;

    case CASE_LABEL:		/* (CASE_LABEL value) */
	fprintf(output_file, "case ");
	decompile_expression(EXPR_ARG1(v));
	fprintf(output_file, ":");
	break;

    case DEFAULT_LABEL:	/* (DEFAULT_LABEL) */
	fprintf(output_file, "default:");
	break;

    case RETURN_STATEMENT:	/* (RETURN_STATEMENT value) */
	fprintf(output_file, "return ");
	if (EXPV_LEFT(v))
	    decompile_expression(EXPV_LEFT(v));
	fprintf(output_file, ";");
	break;

    case EXPR_STATEMENT:
	decompile_expression(EXPR_ARG1(v));
	fprintf(output_file, ";");
    }
    NL;
}

static void decompile_body(expv v)
{
    if(v == NULL){
	fprintf(output_file, "{}");
	return;
    }
    if(EXPV_CODE(v) == LIST){
	fprintf(output_file, "{\n");
	decompile_statement(v);
	fprintf(output_file, "}\n");
    } else
      decompile_statement(v);
}

/* output expression */
static void decompile_expression(expv    v)
{
    ID      id;
    list    lp;
    char   *op;

    if ( v == NULL )
	return;
/*	fatal("decompile_expression: NULL");*/
    switch (EXPV_CODE(v)) {
    default:
	fatal("decompile_expression: unknown code");

    case STRING_CONSTANT:	/* string: "string-name" */
	fprintf(output_file, "\"%s\"", EXPV_STR(v));
	break;

    case INT_CONSTANT:	/* integer constant: if unsigned, output hex */
	if (IS_UNSIGNED(EXPV_TYPE(v)))
	    fprintf(output_file, "((unsigned)0x%x)", EXPV_INT_VALUE(v));
	else
	    fprintf(output_file, "%d", EXPV_INT_VALUE(v));
	break;

    case LONG_CONSTANT:	/* long const. if unsigned, output hex long */
#ifdef SCANNER_READ_L_AS_LL
	{
	    unsigned long lVal = EXPV_ULINT_VALUE(v);
	    if (lVal > UINT_MAX) {
		fprintf(output_file, "0x%08x%08x",
			EXPV_LLINT_HIGH(v), EXPV_LLINT_LOW(v));
	    } else {
		if (IS_UNSIGNED(EXPV_TYPE(v))) {
		    fprintf(output_file, "((unsigned long)0x%xL)", EXPV_ULINT_VALUE(v));
		} else {
		    fprintf(output_file, "%ldL", EXPV_LINT_VALUE(v));
		}
	    }
	    break;
	}
#else
	if (IS_UNSIGNED(EXPV_TYPE(v)))
	    fprintf(output_file, "((unsigned)0x%xL)", EXPV_INT_VALUE(v));
	else
	    fprintf(output_file, "%dL", EXPV_INT_VALUE(v));
	break;
#endif /* SCANNER_READ_L_AS_LL */

    case FLOAT_CONSTANT:	/* float const. */
	fprintf(output_file, "%.21e", EXPV_FLOAT_VALUE(v));
	break;

    case LONGLONG_CONSTANT:	/* longlong const. high and low */
	fprintf(output_file, "0x%08x%08x",
		EXPV_LLINT_HIGH(v), EXPV_LLINT_LOW(v));
	break;

	/* variable reference */
    case VAR_ADDR:
    case PARAM_ADDR:
    case LVAR_ADDR:
	id = lookup_env(v);
	fprintf(output_file, "&%s", ID_NAME(id));
	break;

    case PARAM_VAR:	/* array, etc. output name */
    case LVAR:
    case VAR:
    case LARRAY_ADDR:
    case ARRAY_ADDR:
    case FUNC_ADDR:
    case MOE_CONSTANT:
	id = lookup_env(v);
	fprintf(output_file, "%s", ID_NAME(id));
	break;

	/* primary expr */
    case POINTER_REF:
	switch (EXPV_CODE(EXPV_LEFT(v))) {
	case VAR_ADDR:
	case PARAM_ADDR:
	case LVAR_ADDR:
	    id = lookup_env(EXPV_LEFT(v));
	    fprintf(output_file, "%s", ID_NAME(id));
	    break;

	case PARAM_VAR:
	case LVAR:
	case VAR:
	    fprintf(output_file, "*(");
	    decompile_expression(EXPV_LEFT(v));
	    fprintf(output_file, ")");
	    break;

	default:
	    fprintf(output_file, "*(");
	    decompile_expression(EXPV_LEFT(v));
	    fprintf(output_file, ")");
	}
	break;

    case ARRAY_AREF:	/* array ref. */
	fprintf(output_file, "*(");
	decompile_expression(EXPV_LEFT(v));
	fprintf(output_file, ")");
	break;

    case MEMBER_REF: /* v.member, used for member reference of return value. */
	fprintf(output_file, "((");
	decompile_expression(EXPV_LEFT(v));
	fprintf(output_file, ").%s)", SYM_NAME(EXPV_NAME(EXPR_ARG2(v))));
	break;

    case MEMBER_ADDR:	/* content through the pinter indirect access */
	fprintf(output_file, "&((");
	decompile_expression(EXPV_LEFT(v));
	fprintf(output_file, ")->%s)", SYM_NAME(EXPV_NAME(EXPR_ARG2(v))));
	break;

    case MEMBER_ARRAY_ADDR: 
	fprintf(output_file, "(");
	decompile_expression(EXPV_LEFT(v));
	fprintf(output_file, ")->%s", SYM_NAME(EXPV_NAME(EXPR_ARG2(v))));
	break;

    case ARRAY_REF:	/* array access :(ARRAY_REF x n) */
	decompile_expression(EXPR_ARG1(v));
	if (EXPV_CODE(EXPR_ARG2(v)) == LIST) {
	    FOR_ITEMS_IN_LIST(lp, EXPR_ARG2(v)) {
		fprintf(output_file, "[");
		decompile_expression(LIST_ITEM(lp));
		fprintf(output_file, "]");
	    }
	}
	else {
	    fprintf(output_file, "[");
	    decompile_expression(EXPR_ARG2(v));
	    fprintf(output_file, "]");
	}
	break;

    case ADDR_OF:		/* address ref. : & operator */
	fprintf(output_file, "&(");
	decompile_expression(EXPV_LEFT(v));
	fprintf(output_file, ")");
	break;

    case COMMA_EXPR:	/* comma separated expression */
	fprintf(output_file, "(");
	decompile_expression(EXPV_LEFT(v));
	fprintf(output_file, ",");
	decompile_expression(EXPV_RIGHT(v));
	fprintf(output_file, ")");
	break;

    case FUNCTION_CALL:	/* (FUNCTION_CALL function args-list) */
	if (EXPV_CODE(EXPV_LEFT(v)) == FUNC_ADDR) {
	    id = lookup_env(EXPV_LEFT(v));
	    fprintf(output_file, "%s", ID_NAME(id));
	}
	else {
	    fprintf(output_file, "(*");
	    decompile_expression(EXPV_LEFT(v));
	    fprintf(output_file, ")");
	}
	fprintf(output_file, "(");
	if (EXPV_RIGHT(v)) {
	    FOR_ITEMS_IN_LIST(lp, EXPV_RIGHT(v)) {
		decompile_expression(LIST_ITEM(lp));
		if (LIST_NEXT(lp))
		    fprintf(output_file, ",");
	    }
	}
	fprintf(output_file, ")");
	break;

    case POST_INCR_EXPR:	/* post increment */
	fprintf(output_file, "(");
	decompile_expression(EXPV_LEFT(v));
	fprintf(output_file, ")++");
	break;

    case POST_DECR_EXPR:	/* post decrement */
	fprintf(output_file, "(");
	decompile_expression(EXPV_LEFT(v));
	fprintf(output_file, ")--");
	break;

    case SIZE_OF_EXPR:		/* sizeof: (SIZE_OF_EXPR type-or-expr) */
	fprintf(output_file, "sizeof(");
	decompile_decl_type(EXPV_TYPE(v), NULL);
	fprintf(output_file, ")");
	break;

    case CAST_EXPR:		/* cast expr: (CAST type_name expr ) */
	fprintf(output_file, "((");
	decompile_decl_type(EXPV_TYPE(v), NULL);
	fprintf(output_file, ")");
	fprintf(output_file, "(");
	decompile_expression(EXPV_LEFT(v));
	fprintf(output_file, "))");
	break;

    case CONDITIONAL_EXPR:	/* '?:' expression */
	/* (CONDITIONAL_EXPR condition true-expr false-expr) */
	fprintf(output_file, "(");
	decompile_expression(EXPV_LEFT(v));
	fprintf(output_file, ")?(");
	decompile_expression(EXPV_LEFT(EXPV_RIGHT(v)));
	fprintf(output_file, "):(");
	decompile_expression(EXPV_RIGHT(EXPV_RIGHT(v)));
	fprintf(output_file, ")");
	break;

	/* binary/unary operator */
    case ASSIGN_EXPR:
	op = "=";
	goto binary_op;
    case ASG_PLUS_EXPR:
	op = "+=";
	goto binary_op;
    case ASG_MINUS_EXPR:
	op = "-=";
	goto binary_op;
    case PLUS_EXPR:
	op = "+";
	goto binary_op;
    case MINUS_EXPR:
	op = "-";
	goto binary_op;
    case ASG_DIV_EXPR:
	op = "/=";
	goto binary_op;
    case ASG_MUL_EXPR:
	op = "*=";
	goto binary_op;
    case DIV_EXPR:
	op = "/";
	goto binary_op;
    case MUL_EXPR:
	op = "*";
	goto binary_op;
    case ASG_MOD_EXPR:
	op = "%=";
	goto binary_op;
    case ASG_BIT_AND_EXPR:
	op = "&=";
	goto binary_op;
    case ASG_BIT_OR_EXPR:
	op = "|=";
	goto binary_op;
    case ASG_BIT_XOR_EXPR:
	op = "^=";
	goto binary_op;
    case MOD_EXPR:
	op = "%";
	goto binary_op;
    case BIT_AND_EXPR:
	op = "&";
	goto binary_op;
    case BIT_OR_EXPR:
	op = "|";
	goto binary_op;
    case BIT_XOR_EXPR:
	op = "^";
	goto binary_op;

    case ASG_LSHIFT_EXPR:
	op = "<<";
	goto binary_op;
    case ASG_RSHIFT_EXPR:
	op = ">>";
	goto binary_op;
    case LSHIFT_EXPR:
	op = "<<";
	goto binary_op;
    case RSHIFT_EXPR:
	op = ">>";
	goto binary_op;
    case LOG_GE_EXPR:
	op = ">=";
	goto binary_op;
    case LOG_GT_EXPR:
	op = ">";
	goto binary_op;
    case LOG_LE_EXPR:
	op = "<=";
	goto binary_op;
    case LOG_LT_EXPR:
	op = "<";
	goto binary_op;
    case LOG_EQ_EXPR:
	op = "==";
	goto binary_op;
    case LOG_NEQ_EXPR:
	op = "!=";
	goto binary_op;
    case LOG_AND_EXPR:
	op = "&&";
	goto binary_op;
    case LOG_OR_EXPR:
	op = "||";
	goto binary_op;

    case UNARY_MINUS_EXPR:
	op = "-";
	goto unary_op;
    case BIT_NOT_EXPR:
	op = "~";
	goto unary_op;
    case LOG_NOT_EXPR:
	op = "!";
	goto unary_op;
    }
    return;

binary_op:
    fprintf(output_file, "(");
    decompile_expression(EXPV_LEFT(v));
    fprintf(output_file, ")%s(", op);
    decompile_expression(EXPV_RIGHT(v));
    fprintf(output_file, ")");
    return;

unary_op:
    fprintf(output_file, "%s(", op);
    decompile_expression(EXPV_LEFT(v));
    fprintf(output_file, ")");
    return;
}
