// $Id: XobjectDecompileWriter.java,v 1.54 2003/02/10 08:15:55 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.
//  
//  
//  $
package exc.object;

import java.io.*;
import java.util.*;

//
// decompile Xobject, print in C format
//

public class XobjectDecompileWriter extends PrintWriter {
  public static boolean debugOutputFlag;
  public static boolean printFuncProtoFlag = true;
  public static boolean CxxFlag = false;
  XobjectFile env;

  public XobjectDecompileWriter(OutputStream out,XobjectFile env){
    super(out);
    this.env = env;
  }

  public XobjectDecompileWriter(Writer out,XobjectFile env){
    super(out);
    this.env = env;
  }
    
  void fatal(String msg){
    System.err.println("Fatal XobjectDecompileStream: "+msg);
    Thread.dumpStack();
    System.exit(1);
  }
  
  /* toplevel */
  public void print(Xobject v){
    printWithIdentList(v,env.identList);
  }

  void printWithIdentList(Xobject v,Xobject id_list){
    Ident   id, arg_id;
    String  func_args;
    if(v == null) return;

    switch(v.Opcode()){
    case Xcode.LIST: 	/* nested */
      for(XobjArgs a = v.getArgs(); a != null; a = a.nextArgs())
	printWithIdentList(a.getArg(),id_list);
      break;

    case Xcode.TYPE_DEFINITION:
      printTypeName(v.Type());
      println(" {");
      printWithIdentList(v.getArg(1),v.getArg(0));
      println("};");
      break;
      
    case Xcode.CXX_ATTR_DECL:
      switch(v.getInt()){
      case CxxAttr.EXTERN_C_BEGIN:
	println("extern \"C\" {");
	break;
      case CxxAttr.EXTERN_C_END:
	println("}");
	break;
      default:
	print(CxxAttr.getName(v.getInt()));
	println(":");
      }
      break;

    case Xcode.FUNCTION_DEFINITION:
      /* (FUNCTION_DEFINITION id param-id-list param-decl body) */
      if((id = findIdent(id_list,v.getArg(0))) == null)
	fatal("Function id is not found");

      // printStorageClass(id);
      // printDeclType(id.Type().getRef(),id.getName());
	    
      if(id.Type().isFuncProto() && 
	 printFuncProtoFlag && id.Type().getFuncParam() != null) {
	/* print parameters in ANSI style */
	XobjArgs a = id.Type().getFuncParam().getArgs();
	XobjArgs n = v.getArg(1).getArgs();

	if (n == null) {
	  if (a != null  &&  a.getArg() == null) {
	    func_args = "(...)";
	  } else {
	    func_args = "(void)";
	  }
	} else { 
	  func_args = "(";
	  for (;  n != null;  a = a.nextArgs(), n = n.nextArgs()) {
	    arg_id = (Ident)n.getArg();
	    func_args += makeDeclType(arg_id.Type(),arg_id.getName());
	    if(n.nextArgs() != null) {
	      func_args += ", ";
	    }
	  }
	  if (a != null  &&  a.getArg() == null) {
	    func_args += ", ...";
	  }
	  func_args += ")";
	}
	printStorageClass(id);
	printDeclType(id.Type().getRef(), id.getName()+func_args);
      } else {
	/* print parameters in K & R style */
	func_args = "(";
	if(v.getArg(1) != null){
	  for(XobjArgs a = v.getArg(1).getArgs(); 
	      a != null; a = a.nextArgs()){
	    arg_id = (Ident)a.getArg();
	    func_args += arg_id.getName();
	    if(a.nextArgs() != null) {
		func_args += ",";
	    }
	  }
	}
	func_args += ")";
	printStorageClass(id);
	printDeclType(id.Type().getRef(),id.getName()+func_args);

	/* print decls */
	printIdentList(v.getArg(1));
	printDeclList(v.getArg(2),v.getArg(1));
      }
      /* print body */
      printBody(v.getArg(3),0);
      println();
      break;

    case Xcode.EXT_DECL:
    case Xcode.VAR_DECL:
      printLineNo(v);
      printDeclList(v,id_list);
      break;

    case Xcode.PRAGMA_LINE:
      /* external pramga */
      printLineNo(v);
      // add for va_list
      {
	 String ptn  = "__ompc_output";
	 String str  = v.getArg(0).getString();
	 String arg0 = str.substring(0, ptn.length());
	 char   spr  = str.charAt(ptn.length());
	 if ((arg0.compareTo(ptn) == 0) &&
	     ((spr == ' ') || (spr == '\t'))) {
	   print(str.substring(ptn.length() + 1));
	 } else {
	   print("#pragma " + str);
	 }
      }
      // add for va_list : end
      // print("#pragma ");
      // print(v.getArg(0).getString());
      break;

    default:	/* default, print as statement etc... */
      print(v,0);
      break;
    }
  }

  void print(Xobject v,int l){
    String op;
    if(v == null) return;

    /* indent */
    //	for(int i = 0; i < l; i++) print("  ");
    //	l++;

    switch(v.Opcode()){
    case Xcode.LIST: 	/* nested */
      for(XobjArgs a = v.getArgs(); a != null; a = a.nextArgs())
	print(a.getArg(),l);
      break;
      // 
      // Statement
      //
    case Xcode.COMPOUND_STATEMENT:
      /* (COMPOUND_STATEMENT id-list decl statement-list) */
      printLineNo(v);
      println("{"); 
      printIdentList(v.getArg(0));
      printDeclList(v.getArg(1),v.getArg(0));
      print(v.getArg(2),l);
      println(); print("}"); 
      break;

    case Xcode.IF_STATEMENT: /* (IF_STATMENT cond then-part else-part) */
      printLineNo(v);
      print("if(");
      print(v.getArg(0));
      print(")");
      printBody(v.getArg(1),l);
      if(v.getArg(2) != null){
	print(" else ");
	printBody(v.getArg(2),l);
      }
      break;

    case Xcode.WHILE_STATEMENT: /* (WHILE_STATEMENT cond body) */
      printLineNo(v);
      print("while(");
      print(v.getArg(0));
      print(")");
      printBody(v.getArg(1),l);
      break;

    case Xcode.FOR_STATEMENT:  /* (FOR init cond iter body) */
      printLineNo(v);
      print("for(");
      print(v.getArg(0));	/* init */
      print(";");
      print(v.getArg(1)); /* cond */
      print(";");
      print(v.getArg(2));  /* iter */
      print(")");
      printBody(v.getArg(3),l);  /* body */
      break;
	    
    case Xcode.DO_STATEMENT: /* (DO_STATEMENT body cond) */
      printLineNo(v);
      print("do ");
      printBody(v.getArg(0),l);
      print(" while(");
      print(v.getArg(1));
      print(");");
      if(debugOutputFlag) println();
      break;
    case Xcode.BREAK_STATEMENT:  /* (BREAK_STATEMENT) */
      printLineNo(v);
      print("break; ");
      break;
    case Xcode.CONTINUE_STATEMENT: /* (CONTINUE_STATEMENT) */
      printLineNo(v);
      print("continue; ");
      if(debugOutputFlag) println();
      break;
    case Xcode.GOTO_STATEMENT:	/* (GOTO_STATEMENT label) */
      printLineNo(v);
      print("goto ");
      print(v.getArg(0).getSym());
      print(";");
      if(debugOutputFlag) println();
      break;
    case Xcode.STATEMENT_LABEL: /* (STATEMENT_LABEL label_ident) */
      printLineNo(v);
      print(v.getArg(0).getSym());
      print(":;");
      break;
    case Xcode.SWITCH_STATEMENT: /* (SWITCH_STATEMENT value body) */
      printLineNo(v);
      print("switch(");
      print(v.getArg(0));
      print(")");
      printBody(v.getArg(1),l);
      break;
    case Xcode.CASE_LABEL: 		/* (CASE_LABEL value) */
      printLineNo(v);
      print("case ");
      print(v.getArg(0));
      print(":");
      break;
    case Xcode.DEFAULT_LABEL: /* (DEFAULT_LABEL) */
      printLineNo(v);
      print("default:");
      break;
    case Xcode.RETURN_STATEMENT: /* (RETURN_STATEMENT value) */
      printLineNo(v);
      print("return ");
      if(v.getArg(0) != null) print(v.getArg(0));
      print(";");
      if(debugOutputFlag) println();
      break;
    case Xcode.EXPR_STATEMENT:
      printLineNo(v);
      print(v.getArg(0));
      print(";");
      if(debugOutputFlag) println();
      break;

      //
      // Expression
      //
    case Xcode.STRING_CONSTANT:
      print("\"");
      String s = v.getString();
      for(int i = 0; i < s.length(); i++){
	char c = s.charAt(i);
	if(c < ' ' || c == '"') {
	    /* less than 0100 */
	    if (c < 8) {
		print("\\00"+Integer.toOctalString((int)c));
	    } else {
		print("\\0"+Integer.toOctalString((int)c));
	    }
	}
	else if(c == '\\') print("\\\\");
	else print(c);
      }
      print("\"");
      break;

    case Xcode.INT_CONSTANT:
      if(v.Type().isUnsigned())
	print("((unsigned)0x"+Integer.toHexString(v.getInt())+")");
      else
	print(v.getInt());
      break;
	    
    case Xcode.LONG_CONSTANT:
      if(v.Type().isUnsigned())
	print("((unsigned)0x"+Integer.toHexString(v.getInt())+"L)");
      else {
	print(v.getInt());
	print("L");
      }
      break;
	    
    case Xcode.FLOAT_CONSTANT:
      print(v.getFloat());
      break;
      
    case Xcode.LONGLONG_CONSTANT:
      print("0x"+Long.toHexString(v.getLong()));
      break;

      // variable reference
    case Xcode.VAR_ADDR:
    case Xcode.PARAM_ADDR:
    case Xcode.LVAR_ADDR:
      print("&"+v.getSym());
      break;
    case Xcode.PARAM_VAR:
    case Xcode.LVAR:
    case Xcode.VAR:
    case Xcode.LARRAY_ADDR:
    case Xcode.ARRAY_ADDR:
    case Xcode.FUNC_ADDR:
    case Xcode.MOE_CONSTANT:
      print(v.getSym());
      break;
    case Xcode.REG:
      print(v.getName());
      break;

    case Xcode.FPARAM_ARRAY_ADDR:
    case Xcode.FPARAM_VAR_ADDR:
      print(v.getSym());
      break;
    case Xcode.FPARAM_VAR:
      print("(*"); print(v.getSym()); print(")");
      break;

      /* (FCOMM (IDENT name) (LIST (IDENT comm) (INT_CONSTANT n))) */
    case Xcode.FCOMM_ARRAY_ADDR:
    case Xcode.FCOMM_VAR:
    case Xcode.FCOMM_VAR_ADDR:
      print("(");
      if(v.Opcode() == Xcode.FCOMM_VAR_ADDR) print("&");
      if(v.right().left().Opcode() == Xcode.IDENT)
	print(v.right().left().getSym());
      else{
	print("(");
	print(v.right().left());
	print(")");
      }
      print("._");
      print(v.right().right().getInt());
      print(".");
      print(v.left().getSym());
      print(")");
      break;

      // primary expr
    case Xcode.POINTER_REF:	
      switch(v.left().Opcode()){
      case Xcode.VAR_ADDR:
      case Xcode.PARAM_ADDR:
      case Xcode.LVAR_ADDR:
	print(v.left().getSym());
	break;
      case Xcode.PARAM_VAR:
      case Xcode.LVAR:
      case Xcode.VAR:
	print("*");
	print(v.left());
	break;
      case Xcode.MEMBER_ADDR:
	print("(");
	print(v.left().left());
	print(")->");
	print(v.left().getArg(1).getSym());
	break;
	/*
	  case Xcode.PLUS_EXPR:
	  print(v.left().left());
	  print("[");
	  print(v.left().right());
	  print("]");
	  break;
	  */
      default:
	print("*(");
	print(v.left());
	print(")");
      }
      break;
    case Xcode.ARRAY_AREF:
      print("*(");
      print(v.left());
      print(")");
      break;

    case Xcode.MEMBER_REF:	/* (MEMBER_REF v member), v.member */
      print("((");
      print(v.left());
      print(").");
      print(v.getArg(1).getSym());
      print(")");
      break;
	    
    case Xcode.MEMBER_ADDR:
      print("&((");
      print(v.left());
      print(")->");
      print(v.getArg(1).getSym());
      print(")");
      break;

    case Xcode.MEMBER_ARRAY_ADDR:
      print("(");
      print(v.left());
      print(")->");
      print(v.getArg(1).getSym());
      break;

    case Xcode.ARRAY_REF:	/* (ARRAY_REF x n) */
      print(v.getArg(0));
      if(v.getArg(1).Opcode() == Xcode.LIST){
	for(XobjArgs a = v.getArg(1).getArgs(); 
	    a != null; a = a.nextArgs()){
	  print("[");
	  print(a.getArg());
	  print("]");
	}
      } else {
	print("[");
	print(v.getArg(1));
	print("]");
      }
      break;

    case Xcode.ADDR_OF:	/* & operator */
      print("&(");
      print(v.left());
      print(")");
      break;

    case Xcode.COMMA_EXPR:	// extended 
      print("(");
      for(XobjArgs a = v.getArgs(); a != null; a = a.nextArgs()){
	print(a.getArg());
	if(a.nextArgs() != null) print(",");
      }
      print(")");
      break;

    case Xcode.FUNCTION_CALL: /* (FUNCTION_CALL function args-list) */
      // add for va_list
      if (v.left().Opcode() == Xcode.FUNC_ADDR) {
	if (v.left().getSym() == "__ompc_output") {
	  if (v.right() != null) {
	    Xobject  arg0 = v.right().getArgs().getArg();
	    XobjArgs args = v.right().getArgs().nextArgs();
	    String   fmt  = arg0.getString();

	    for (int from = 0; from < fmt.length(); ) {
	      int index = fmt.indexOf("%", from);
	      if (index == -1) {
		print(fmt.substring(from));
		break;
	      } else {
		print(fmt.substring(from,index));
		from = index + 2;
		if (fmt.charAt(index+1) == 's') {
		  print (args.getArg());
		} else if (fmt.charAt(index+1) == 't') {
		  printDeclType (args.getArg().Type(), "");
		}
		args = args.nextArgs();
	      }
	    }
	  }
	  break;
	}
      }
      // add for va_list : end

      if(v.left().Opcode() == Xcode.FUNC_ADDR){
	print(v.left().getSym());
      } else {
	print("(");  // bug fix
	print(v.left());
	print(")");
      }
      printArgList(v.right());
      break;

    case Xcode.POST_INCR_EXPR:
      print("(");
      print(v.left());
      print(")++");
      break;
    case Xcode.POST_DECR_EXPR:
      print("(");
      print(v.left());
      print(")--");
      break;

    case Xcode.SIZE_OF_EXPR:	/* (SIZE_OF_EXPR type-or-expr) */
      print("sizeof(");
      printDeclType(v.Type(),null);
      print(")");
      break;

    case Xcode.CAST_EXPR:		/* (CAST type_name expr ) */
      print("((");
      printDeclType(v.Type(),null);
      print(")(");
      print(v.left());
      print("))");
      break;

    case Xcode.NEW_EXPR:
      print("(new (");
      printDeclType(v.Type().getRef(),null);
      print(")");
      printArgList(v.left());
      print(")");
      break;

    case Xcode.CONDITIONAL_EXPR: 
      /* (CONDITIONAL_EXPR condition true-expr false-expr) */
      print("(");
      print(v.left());
      print(")?(");
      print(v.right().left());
      print("):(");
      print(v.right().right());
      print(")");
      break;

    case Xcode.ASSIGN_EXPR:
      op = "=";  printBinaryExpr(op,v.left(),v.right()); break;
    case Xcode.ASG_PLUS_EXPR:  
      op = "+=";  printBinaryExpr(op,v.left(),v.right()); break;
    case Xcode.ASG_MINUS_EXPR:  
      op = "-=";  printBinaryExpr(op,v.left(),v.right()); break;
    case Xcode.PLUS_EXPR:  
      op = "+";  printBinaryExpr(op,v.left(),v.right()); break;
    case Xcode.MINUS_EXPR:  
      op = "-";  printBinaryExpr(op,v.left(),v.right()); break;
    case Xcode.ASG_DIV_EXPR:  
      op = "/=";  printBinaryExpr(op,v.left(),v.right()); break;
    case Xcode.ASG_MUL_EXPR:  
      op = "*=";  printBinaryExpr(op,v.left(),v.right()); break;
    case Xcode.DIV_EXPR:  
      op = "/";  printBinaryExpr(op,v.left(),v.right()); break;
    case Xcode.MUL_EXPR:
      op = "*";  printBinaryExpr(op,v.left(),v.right()); break;
    case Xcode.UNARY_MINUS_EXPR:
      op = "-";  printUnaryExpr(op,v.left()); break;
    case Xcode.ASG_MOD_EXPR:
      op = "%=";  printBinaryExpr(op,v.left(),v.right()); break;
    case Xcode.ASG_BIT_AND_EXPR:
      op = "&=";  printBinaryExpr(op,v.left(),v.right()); break;
    case Xcode.ASG_BIT_OR_EXPR:
      op = "|=";  printBinaryExpr(op,v.left(),v.right()); break;
    case Xcode.ASG_BIT_XOR_EXPR:
      op = "^=";  printBinaryExpr(op,v.left(),v.right()); break;
    case Xcode.MOD_EXPR:
      op = "%";  printBinaryExpr(op,v.left(),v.right()); break;
    case Xcode.BIT_AND_EXPR:
      op = "&";  printBinaryExpr(op,v.left(),v.right()); break;
    case Xcode.BIT_OR_EXPR:
      op = "|";  printBinaryExpr(op,v.left(),v.right()); break;
    case Xcode.BIT_XOR_EXPR:
      op = "^";  printBinaryExpr(op,v.left(),v.right()); break;
    case Xcode.BIT_NOT_EXPR:
      op = "~";  printUnaryExpr(op,v.left()); break;

    case Xcode.ASG_LSHIFT_EXPR:
      op = "<<=";  printBinaryExpr(op,v.left(),v.right()); break;
    case Xcode.ASG_RSHIFT_EXPR:
      op = ">>=";  printBinaryExpr(op,v.left(),v.right()); break;
    case Xcode.LSHIFT_EXPR:
      op = "<<";  printBinaryExpr(op,v.left(),v.right()); break;
    case Xcode.RSHIFT_EXPR:
      op = ">>";  printBinaryExpr(op,v.left(),v.right()); break;
    case Xcode.LOG_GE_EXPR:
      op = ">=";  printBinaryExpr(op,v.left(),v.right()); break;
    case Xcode.LOG_GT_EXPR:
      op = ">";  printBinaryExpr(op,v.left(),v.right()); break;
    case Xcode.LOG_LE_EXPR:
      op = "<=";  printBinaryExpr(op,v.left(),v.right()); break;
    case Xcode.LOG_LT_EXPR:
      op = "<";  printBinaryExpr(op,v.left(),v.right()); break;
    case Xcode.LOG_EQ_EXPR:
      op = "==";  printBinaryExpr(op,v.left(),v.right()); break;
    case Xcode.LOG_NEQ_EXPR:
      op = "!=";  printBinaryExpr(op,v.left(),v.right()); break;
    case Xcode.LOG_AND_EXPR:
      op = "&&";  printBinaryExpr(op,v.left(),v.right()); break;
    case Xcode.LOG_OR_EXPR:
      op = "||";  printBinaryExpr(op,v.left(),v.right()); break;  
    case Xcode.LOG_NOT_EXPR:
      op = "!";  printUnaryExpr(op,v.left()); break;
    case Xcode.OMP_PRAGMA:
    case Xcode.TEA_PRAGMA:
      // System.err.println("OpenMP pragma is ignored!!!");
      print(v.getArg(2));
      break;

    case Xcode.ALLOCA:
      /* (ALLOCA (IDENT ) */
      if(v.Type().isArray() && v.Type().getArrayDim() < 0){
	/* adjustable array */
	Xtype t = v.Type();
	print(v.operand().getName());
	print("=(");
	printDeclType(t.getRef(),null);
	print("*)alloca(("); print(t.getArrayAdjSize()); print(")");
	print("*"+t.getRef().getSize()+")");
	return;
      }
      break;
      
    default:
      /* fatal("print: unknown decopmile = "+v); */
      printUserCode(v);
    }
  }

  /* default action, which can be override */
  public void printUserCode(Xobject v){
    print("_user_xcode(/* "+v+" */)");
  }

  void printArgList(Xobject v){
    print("(");
    if(v != null){
	for(XobjArgs a = v.getArgs(); a != null; a=a.nextArgs()){
	  print(a.getArg());
	  if(a.nextArgs() != null) print(",");
	}
    }
    print(")");
  }

  void printLineNo(Xobject v){
    LineNo ln = v.getLineNo();
    if(ln == null) return;
    println();
    println("# "+ln.lineNo()+" \""+ln.fileName()+"\"");
  }

  void printBody(Xobject v,int l){
    if(v == null){
      print("{}");  // null body
      return;
    }
    if(v.Opcode() == Xcode.LIST){
      print("{");
      print(v,l);
      println(); print("}");
    } else
      print(v,l);
    if(debugOutputFlag) println();
  }

  void printDeclList(Xobject v,Xobject id_list){
    Ident id;
    if(v == null) return;
    switch(v.Opcode()){
    case Xcode.LIST: 	/* nested */
      for(XobjArgs a = v.getArgs(); a != null; a = a.nextArgs())
	printDeclList(a.getArg(),id_list);
      break;

    case Xcode.EXT_DECL:
    case Xcode.VAR_DECL:
      Xobject s = v.getArg(0);
      if((id = findIdent(id_list,s)) == null &&
	 (id = findIdent(env.getGlobalIdentList(),s)) == null)
	fatal("Variable id is not found, "+s);
      if(id.getStorageClass() == StorageClass.SNULL)
	break;	// removed decl
      if(v.Opcode() == Xcode.EXT_DECL &&
	 id.getStorageClass() == StorageClass.EXTDEF)
	print("extern ");
      printIdentDecl(id);
      if(v.getArg(1) != null){
	print("=");
	printInitializer(v.getArg(1));
      }
      println(";");
      break;

    case Xcode.TYPE_DEFINITION:
      printWithIdentList(v,id_list);
      break;

      // ignore it
      // default:
      // fatal("printDeclList: unknown code");
    }
  }

  void printStorageClass(Ident id){
    switch(id.getStorageClass()){
    case StorageClass.AUTO: 
    case StorageClass.TEMP:
    case StorageClass.CTEMP:
      print("auto ");
      break;
    case StorageClass.SNULL: // member
      printDeclType(id.Type(),id.getName());
      if(id.getBitField() != 0) print(":"+id.getBitField());
      return;
    case StorageClass.PARAM: 
    case StorageClass.EXTDEF:
    case StorageClass.MEMBER:
      break;
    case StorageClass.EXTERN: 
      print("extern ");
      break;
    case StorageClass.STATIC: 
      print("static ");
      break;
    case StorageClass.REGISTER: 
      print("register ");
      break;
    case StorageClass.REG:
      printDeclType(id.Type(),id.getName());
      return;
    case StorageClass.FPARAM:
      if(id.Type().isScalar()){
	printDeclType(Xtype.Pointer(id.Type()),id.getName());
	return;
      }
      break;
    case StorageClass.FCOMM:
      return;  /* nothing */
    case StorageClass.TAGNAME:
    default:
      fatal("printStorageClass: bad class,"+
	    StorageClass.getName(id.getStorageClass()));
    }
  }

  void printIdentDecl(Ident id){
    switch(id.getStorageClass()){
    case StorageClass.AUTO: 
      /* fall through */
    case StorageClass.TEMP:
    case StorageClass.CTEMP:
      print("auto ");
      break;
    case StorageClass.SNULL: // member
      printDeclType(id.Type(),id.getName());
      if(id.getBitField() != 0) print(":"+id.getBitField());
      return;
    case StorageClass.PARAM: 
    case StorageClass.EXTDEF:
    case StorageClass.MEMBER:
      break;
    case StorageClass.EXTERN: 
      print("extern ");
      // check external run-time function for C++
      if(CxxFlag && !id.isDeclared() && 
	 id.Type().isFunction() && !id.Type().isFuncProto())
	print("\"C\" ");
      break;
    case StorageClass.STATIC: 
      print("static ");
      break;
    case StorageClass.REGISTER: 
      print("register ");
      break;
    case StorageClass.REG:
      printDeclType(id.Type(),id.getName());
      return;
    case StorageClass.FPARAM:
      if(id.Type().isScalar()||id.Type().isStruct()){
	printDeclType(Xtype.Pointer(id.Type()),id.getName());
	return;
      }
      break;
    case StorageClass.FCOMM:
    case StorageClass.TYPEDEF_NAME:
      return;  /* nothing */
    case StorageClass.TAGNAME:
    default:
      fatal("printIdentDecl: bad class,"+
	    StorageClass.getName(id.getStorageClass()));
    }

    printDeclType(id.Type(),id.getName());
  }

  String makeDeclType(Xtype type,String name) {
    String decltype = "";
    Xtype t;
    Stack nested_decls = new Stack();

    for(t = type; t != null; t = t.getRef()){
      if(t.getKind() == Xtype.POINTER ||
	 t.getKind() == Xtype.FUNCTION ||
	 t.getKind() == Xtype.ARRAY)
	nested_decls.push(t);
      else break;
    }
    if(t == null) fatal("makeDeclType: no base type");
    else {
      /* qualify type: ex. const char *p; */
      if(t.isConst())    decltype += "const ";
      if(t.isVolatile()) decltype += "volatile ";
      decltype += makeTypeName(t);
    }
    decltype += " " + makeDeclaratorRec(nested_decls,name);

    return decltype;
  }

  void printDeclType(Xtype type, String name) {
    print(makeDeclType(type,name));
  }
    
  String makeDeclaratorRec(Stack decls,String name){
    String decltype = "";
    boolean pred = false;

    if(decls.empty()) {
      if(name != null) {
	return name;
      } else {
        return "";
      }
    }
    Xtype t = (Xtype) decls.pop();
    if(!decls.empty() && t.getKind() != Xtype.POINTER &&
       ((Xtype)decls.peek()).getKind() == Xtype.POINTER)
      pred = true;
    
    switch(t.getKind()){
    case Xtype.POINTER:
      /* qualify pointer itself: ex. char * const p; */
      decltype += "*";
      if(t.isConst())    decltype += "const ";
      if(t.isVolatile()) decltype += "volatile ";
      decltype += makeDeclaratorRec(decls,name);
      break;
    case Xtype.FUNCTION:
      if(pred) decltype += "(";
      decltype += makeDeclaratorRec(decls,name);
      if(pred) decltype += ")";
      if(t.isFuncProto() && printFuncProtoFlag && t.getFuncParam() != null){
	decltype += makeFuncProtoArgs(t.getFuncParam());
      } else {
	decltype += "()";
      }
      break;
    case Xtype.ARRAY:		/* array type */
      if(!decls.empty() && 
	 ((Xtype)decls.peek()).getKind() == Xtype.FUNCTION){
	/* function that returns array */
	decltype += "*";
	decltype += makeDeclaratorRec(decls,name);
	break;
      }

      if(t.getArrayDim() < 0){	/* adjustable array in Fortran */
	decltype += "(";
	decltype += "*";
	decltype += makeDeclaratorRec(decls,name);
	decltype += ")";
	break;
      }
      if(pred) decltype += "(";
      decltype += makeDeclaratorRec(decls,name);
      if(pred) decltype += ")";
      if(t.getArrayDim() <= 0) {
	decltype += "[]";
      } else {
	decltype += "["+t.getArrayDim()+"]";
      }
      break;
    default:
      fatal("printDeclaratorRec");
    }

    return decltype;
  }

  String makeFuncProtoArgs(Xobject args){
    String func_args;

    if(args.getArgs() == null) {
      func_args = "(void)";
    } else {
      func_args = "(";
      for(XobjArgs a = args.getArgs(); a != null; a = a.nextArgs()){
	if(a.getArg() == null){
	  func_args += "...";
	  break;
	} else {
	  if(a.getArg().Opcode() == Xcode.IDENT)
	    func_args += makeDeclType(a.getArg().Type(),a.getArg().getName());
	  else
	    func_args += makeDeclType(a.getArg().Type(),null);
	}
	if(a.nextArgs() != null) {
	    func_args += ",";
	}
      }
      func_args += ")";
    }
    return func_args;
  }
    
  String makeTypeName(Xtype type){
    String typename = "";

    // replace true reference
    if(type.copyed != null) type = type.copyed;

    switch(type.getKind()){
    case Xtype.BASIC:
      switch(type.getBasicType()){
      case BasicType.VOID: 
	typename += "void"; break;
      case BasicType.CHAR: 
	typename += "char"; break;
      case BasicType.SHORT: 
	typename += "short"; break;
      case BasicType.LONG: 
	typename += "long"; break;
      case BasicType.LONGLONG: 
	typename += "long long"; break;
      case BasicType.UNSIGNED_CHAR: 
	typename += "unsigned char"; break;
      case BasicType.UNSIGNED_SHORT: 
	typename += "unsigned short"; break;
      case BasicType.UNSIGNED_LONG: 
	typename += "unsigned long"; break;
      case BasicType.UNSIGNED_LONGLONG: 
	typename += "unsigned long long"; break;
      case BasicType.FLOAT: 
	typename += "float"; break;
      case BasicType.DOUBLE: 
	typename += "double"; break;
      case BasicType.LONG_DOUBLE: 
	typename += "long double"; break;
      case BasicType.UNSIGNED_INT: 
	typename += "unsigned"; break;
      case BasicType.INT:  
	typename += "int"; break;
      case BasicType.SIGNED: 
	typename += "signed"; break;
      default: fatal("makeTypeName: bad basic type "+type);
      }
      return typename;
    case Xtype.STRUCT:
      typename += "struct ";
      if(type.getTagName() != null) {
	typename += type.getTagName();
      } else {
	typename += "_S" + Integer.toHexString(type.Id());
      }
      break;
    case Xtype.UNION:
      typename += "union ";
      if(type.getTagName() != null) {
        typename += type.getTagName();
      } else {
	typename += "_U" + Integer.toHexString(type.Id());
      }
      break;
    case Xtype.CLASS:
      typename += "class ";
      if(type.getTagName() != null) {
	typename += type.getTagName();
      } else {
	typename += "_C" + Integer.toHexString(type.Id());
      }
      break;
    case Xtype.ENUM:
      typename += "enum ";
      if(type.getTagName() != null) {
        typename += type.getTagName();
      } else {
	typename += "_E" + Integer.toHexString(type.Id());
      }
      break;
    default:
      fatal("makeTypeName: undefined Type found");
    }

    return typename;
  }

  void printTypeName(Xtype type){
    print(makeTypeName(type));
  }

  void printInitializer(Xobject init) {
    if(init.Opcode() == Xcode.LIST){
      int n = 0;
      print("{");
      for(XobjArgs a = init.getArgs(); a != null; a = a.nextArgs()){
	printInitializer(a.getArg());
	print(",");
	n++;
	if (n % 16 == 0) {
	  print("\n");
	}
      }
      print("}");
    } else print(init);
  }
    
  void printBinaryExpr(String op,Xobject left,Xobject right){
    print("(");
    print(left);
    print(")");
    print(op);
    print("(");
    print(right);
    print(")");
  }

  void printUnaryExpr(String op,Xobject opd){
    print(op);
    print("(");
    print(opd);
    print(")");
    return;
  }
    
  Ident findIdent(Xobject id_list,Xobject s){
    if(id_list == null || s == null) return null;
    if(s.Opcode() != Xcode.IDENT) fatal("findIdent: not IDENT");
    for(XobjArgs a = id_list.getArgs(); a != null; a=a.nextArgs()){
      Ident id = (Ident)a.getArg();
      if(id.getStorageClass() == StorageClass.TAGNAME) continue;
      if(s.getSym().equals(id.getName())) return id;
    }
    return null;
  }

  public void printIdentList(Xobject id_list){
    Ident id;
    Xtype type;

    if(id_list == null) return;

    if(!CxxFlag) printStructTAGNAME(id_list);

    /* gcc bug!! */
    for(XobjArgs a = id_list.getArgs(); a != null; a=a.nextArgs()){
      id = (Ident) a.getArg();

      if(id.getStorageClass() == StorageClass.TAGNAME) continue;
      if(id.getStorageClass() == StorageClass.SNULL) continue;

      // for non declared ident
      if(!id.isDeclared() || 
	 id.getStorageClass() == StorageClass.TEMP ||
	 id.getStorageClass() == StorageClass.CTEMP){
	/* gcc bug fix */
	if(XobjectFile.gcc_huge_common_bug &&
	   id.getStorageClass() == StorageClass.EXTDEF &&
	   id.Type().getSize() >= 0x10000000){
	  id.setStorageClass(StorageClass.EXTERN);
	  println("asm(\"    .comm "+id.getName()+
		  ","+id.Type().getSize()+",1 \");/*gcc bugfix*/");
	  
	}
	/* gcc bug fix */
	printIdentDecl(id);
	println(";");
      }
    }
  }
  
  void printStructTAGNAME(Xobject id_list){
    Ident id;
    Xtype type;
    
    /* scan for tag name */
    for(XobjArgs a = id_list.getArgs(); a != null; a=a.nextArgs()){
      id = (Ident) a.getArg();

      if(id.getStorageClass() != StorageClass.TAGNAME) continue;
      // only for TAGNAME

      type = id.Type();
      // if(!type.isUsed()) continue;

      switch(type.getKind()){
      case Xtype.STRUCT:
      case Xtype.UNION:
	printTypeName(type);
	println(";");
      }
    }

    /* declaration for struct body */
    for(XobjArgs a = id_list.getArgs(); a != null; a=a.nextArgs()){
      id = (Ident) a.getArg();

      if(id.getStorageClass() != StorageClass.TAGNAME) continue;
      // only for TAGNAME

      type = id.Type();
      //  if(!type.isUsed()) continue;

      switch(type.getKind()){
      case Xtype.STRUCT:
      case Xtype.UNION:
	if(type.getMemberList().getArgs() == null) break;
	printTypeName(type);
	println(" {");
	for(XobjArgs aa = type.getMemberList().getArgs(); aa != null; 
	    aa=aa.nextArgs()){
	  printIdentDecl((Ident)aa.getArg());
	  println(";");
	}
	println(" };");
	break;
      case Xtype.ENUM:
	if(type.getMoeList() == null) break;
	printTypeName(type);
	println(" {");
	for(XobjArgs a1 = type.getMoeList().getArgs(); 
	    a1 != null; a1=a1.nextArgs()){
	  if((id = findIdent(id_list,a1.getArg())) == null)
	    fatal("Enum ID is not found");
	  print(a1.getArg().getSym());
	  if(id.getValue() != null){
	    print("=");
	    print(id.getAddr());
	  }
	  println(",");
	}
	println( " };");
	break;
      }
    }
  }
}





