// $Id: XobjectReader.java,v 1.10 2002/02/16 20:55:31 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.Hashtable;

public class XobjectReader extends BufferedReader {
  final static char EOF = (char)-1;
  final static int BUF_SIZE = 256;
  char input_buffer[];
  XobjectFile file_env;

  static int CharTable[] = InitCharTable();
  final static int SPACE_CHAR = 0x1;
  final static int WORD_CHAR = 0x2;
  final static int DIGIT_CHAR = 0x4;
  final static int XDIGIT_CHAR = 0x8;

  boolean ispeek = false;
  char peekc;

  static Hashtable XcodeTable = InitXcodeTable();

  static Hashtable InitXcodeTable(){
    Hashtable table = new Hashtable(Xcode.MAX_CODE*2,0.5f);
    for(int i = 0; i < Xcode.MAX_CODE; i++)
      table.put(Xcode.name[i].intern(),new Integer(i));
    return table;
  }

  static int [] InitCharTable(){
    int ct[] = new int[128];
    char c;
    setCharTable(ct,' ',SPACE_CHAR);
    setCharTable(ct,'\t',SPACE_CHAR);
    setCharTable(ct,'\n',SPACE_CHAR);
    setCharTable(ct,'\r',SPACE_CHAR);
    setCharTable(ct,'\f',SPACE_CHAR);
    for(c = 'a'; c <= 'z'; c++) setCharTable(ct,c,WORD_CHAR);
    for(c = 'A'; c <= 'Z'; c++) setCharTable(ct,c,WORD_CHAR);
    setCharTable(ct,'_',WORD_CHAR);
    for(c = '0'; c <= '9'; c++) setCharTable(ct,c,DIGIT_CHAR|XDIGIT_CHAR);
    for(c = 'a'; c <= 'f'; c++) setCharTable(ct,c,XDIGIT_CHAR);
    for(c = 'A'; c <= 'F'; c++) setCharTable(ct,c,XDIGIT_CHAR);
    return ct;
  }

  static void setCharTable(int ct[],char c,int a){
    ct[(int)c] |= a;
  }

  static boolean isSpaceChar(char c){
    return (CharTable[((int)c)&0x7F]&SPACE_CHAR) != 0;
  }

  static boolean isWordChar(char c){
    return (CharTable[((int)c)&0x7F]&(WORD_CHAR|DIGIT_CHAR)) != 0;
  }

  static boolean isXdigitChar(char c){
    return (CharTable[((int)c)&0x7F]&XDIGIT_CHAR) != 0;
  }

  static boolean isDigitChar(char c){
    return (CharTable[((int)c)&0x7F]&DIGIT_CHAR) != 0;
  }

  // constructor
  public XobjectReader(Reader in,XobjectFile file_env){
    super(in,4096);
    input_buffer = new char[BUF_SIZE];
    this.file_env = file_env;
  }

  void fatal(String msg){
    System.err.println("Fatal XobjectReader:"+msg);
    Thread.dumpStack();
    System.exit(1);
  }
    
  char getc(){
    try {
      if(ispeek){
	ispeek = false;
	return peekc;
      } else {
	//char c = (char) read();
	//System.out.print("["+c+"]");
	//return c;
	return (char) read();
      }
    } catch(IOException e){
      fatal("I/O error");
    }
    return '\0';
  }

  void ungetc(char c){
    ispeek = true;
    peekc = c;
  }

  // skip spaces
  char skipSpace() {
    char c;
    while((c = getc()) != EOF){
      if(isSpaceChar(c)) continue;
      switch(c){
      default:
	return(c);
      case ';':	/* lisp style comment */
      case '#':
	while((c = getc()) != '\n'){
	  if(c == EOF) return EOF;
	}
	break;
      case '/':		/* C style comment */
	if((c = getc()) != '*') {
	  ungetc(c);
	  return('/');
	}
	while((c = getc()) != EOF){
	  if(c == '*' && (c = getc()) == '/'){
	    c = 0;
	    break;
	  }
	}
	if(c == EOF){
	  fatal("unexpected EOF");
	}
      }
    }
    return(c);
  }

  /* read integer value */
  int readInt(){
    char c = skipSpace();
    if(c == '0'){
      c = getc();
      if(c == 'x' || c == 'X') c = getc();
      else {
	ungetc(c);
	return 0;
      }
    }
    if(!isXdigitChar(c)) fatal("integer is expected");
    int l = 0;
    do {
      input_buffer[l++] = c;
      c = getc();
    } while(isXdigitChar(c));
    ungetc(c);
    // convert to interger
    int i = 0;
    for(int k = 0; k < l; k++){
      c = input_buffer[k];
      if(c >= '0' && c <= '9') i = (i<<4)+(c-'0');
      else if(c >= 'a' && c <= 'f') i = (i<<4)+(c-'a')+10;
      else if(c >= 'A' && c <= 'F') i = (i<<4)+(c-'A')+10;
      else fatal("readInt: bad char");
    }
    return i;
  }

  String readName(){
    char c = skipSpace();
    if(!isWordChar(c)){
      fatal("readName: bad char");
    }
    int i = 0;
    while(true){
      input_buffer[i++] = c;
      if(i >= BUF_SIZE) fatal("too long name");
      c = getc();
      if(!isWordChar(c)) break;
    }
    ungetc(c);
    String s = new String(input_buffer,0,i);
    return s.intern();
  }

  String readString() {
    char c = skipSpace();
    if(c != '"') fatal("string expected");
    StringBuffer buf = new StringBuffer();
    while((c = getc()) != '"'){
      if(c == '\\'){
	c = getc();
	if(c >= '0' && c <= '7'){
	  /* \ooo */
	  int n = 0;
	  for(int i = 0; i < 3; i++){
	    n = n*8 + (c - '0');
	    c = getc();
	    if(c < '0' || c > '7') break;
	  }
	  ungetc(c);
	  c = (char)n;
	} 
      }
      buf.append(c);
    }
    return new String(buf);
  }

  String readFileName(){
    char c;
    int i = 0;
    while(!isSpaceChar(c = getc())){
      input_buffer[i++] = c;
    } 
    ungetc(c);
    return new String(input_buffer,0,i);
  }

  public Xobject readObject(){
    char c =skipSpace();
    if(c == EOF) return(null);
    if(c != '(') fatal("bad expression at '"+c+"'["+(int)c+"]");
    c = skipSpace();
    if(c == ')'){	/* () */
      return null;
    } 
    ungetc(c);

    String name = readName();
    int opcode;
    Xobject v;
    Xtype type;

    /* search name */
    /*
      for(opcode = 0; opcode < Xcode.MAX_CODE; opcode++){
      if(name.equals(Xcode.getName(opcode))) break;
      }
      if(opcode >= Xcode.MAX_CODE) fatal("unknown code name, '"+name+"'");
      */
    Integer Code = (Integer)XcodeTable.get(name);
    if(Code == null) fatal("unknown code name, '"+name+"'");
    opcode = Code.intValue();

    if((c = getc()) == ':'){	/* find type */
      type = readType();
    } else  {
      ungetc(c);
      type = null;
    }

    if(Xcode.kind[opcode] == 'T'){
      switch(opcode){
      case Xcode.ID_LIST:
	v = readIdentList();
	break;
      case Xcode.IDENT:		/* NAME */
      case Xcode.VAR_ADDR:	/* ICON */
      case Xcode.LVAR_ADDR:
      case Xcode.PARAM_ADDR:
      case Xcode.PARAM_VAR:
      case Xcode.LVAR:
      case Xcode.VAR:
      case Xcode.ARRAY_ADDR:
      case Xcode.LARRAY_ADDR:
      case Xcode.FUNC_ADDR:
      case Xcode.MOE_CONSTANT:
      case Xcode.FPARAM_ARRAY_ADDR:
      case Xcode.FPARAM_VAR_ADDR:
      case Xcode.FPARAM_VAR:
	v = new XobjString(opcode,type,readName());
	break;
      case Xcode.CXX_ATTR_DECL:
      case Xcode.INT_CONSTANT:
      case Xcode.LONG_CONSTANT:
      case Xcode.REG:
	v = new XobjInt(opcode,type,readInt());
	break;
      case Xcode.STRING_CONSTANT:
	v = new XobjString(opcode,type,readString());
	break;
      case Xcode.LONGLONG_CONSTANT:
      case Xcode.FLOAT_CONSTANT:
	int h = readInt();
	int l = readInt();
	v = new XobjLong(opcode,type,h,l);
	break;
      default:
	v = null;
	fatal("unknown terminal");
      }
      if((c = skipSpace()) != ')') fatal("')' is expected '"+c+"'");
      return v;
    }

    /* input list */
    v = new XobjList(opcode,type);
    if((c = getc()) == '@'){ /* lineno info */
      c = getc();
      if(isDigitChar(c)){
	int ln = 0;
	String fname = null;
	do {
	  ln = ln*10 + c - '0';
	  c = getc();
	} while(isDigitChar(c));
	if(c == '.'){
	  int i = 0;
	  while(!isSpaceChar(c = getc())){
	    input_buffer[i++] = c;
	  } 
	  fname = new String(input_buffer,0,i);
	}
	v.setLineNo(LineNo.make(fname,ln));
      }
    }
    ungetc(c);

    while((c = skipSpace()) != ')'){
      ungetc(c);
      v.add(readObject());
    }
    return(v);
  }

  public Xobject readIdentList(){
    Xobject id_list = new XobjList(Xcode.ID_LIST);
    String s;
    int stg_class;
    char c;
    Xobject v;
    Xtype type;
	
    while(true){
      c = skipSpace();
      if(c != '[') break;
	    
      /* [ name storage_class type base ] */
      c = skipSpace();
      if(c == '*') s = null;
      else {
	ungetc(c);
	s = readName();
      }
	    
      //	    System.out.println("id list name="+s);
	    
      /* storage_class */
      c = skipSpace();
      stg_class = 0;
      if(c != '*'){
	ungetc(c);
	stg_class = StorageClass.findClass(readName());
      }
	    
      /* type */
      c = skipSpace();
      if(c != '*'){
	ungetc(c);
	type = readType();
      } else type = null;
	    
      /* base */
      v = readObject();
      Ident id = new Ident(s,stg_class,type,v);
	    
      c = skipSpace();
      if(isDigitChar(c)){
	int i = 0;
	do {
	  i = i*10 + c - '0';
	  c = getc();
	} while(isDigitChar(c));
	ungetc(c);
	id.setBitField(i);
	c = skipSpace();
      } 
      if(c == '*') c = skipSpace();
      else id.Declared();

      if(c != ']') fatal("bad ident desc");
	    
      id_list.add((Xobject)id);
	    
      /* if id is tagname, link to type_desc */
      if(stg_class == StorageClass.TAGNAME) type.setTagIdent(id);
    }
    ungetc(c);
    return id_list;
  }
    
  Xtype readType(){
    return file_env.findType(readName());
  }
}



