/* symbol.cc */

#include "symbol.h"
#include <iostream.h>

dc_symbol_block sb; /* declared as extern in symbol.h */

dc_symbol_tag dc_symbol_block::define_symbol( cstring s_str ) {
  static dc_symbol_tag symbol_num = 1;
  if( !symbol_num ) {
    cerr << "define_symbol -- symbol counter overrun\n";
    exit( 1 );
  }
  
  dic_item s_item;
  if( ( s_item = symbol_dict.lookup( s_str ) ) != nil ) {
    return symbol_dict.inf( s_item );
  }
  dc_symbol_tag s_tag = symbol_num++;
  symbol_dict.insert( s_str, s_tag );
  symbol_rdict.insert( s_tag, s_str );
  return s_tag;
}

dc_symbol_tag dc_symbol_block::lookup_symbol( cstring s_str ) const {
  dic_item s_item;
  if( ( s_item = symbol_dict.lookup( s_str ) ) != nil ) {
    return symbol_dict.inf( s_item );
  }
  return undef_symbol;
}

dc_svec_tag dc_symbol_block::define_svec( cstring e_str ) {
  static dc_svec_tag svec_num = 1;
  if( !svec_num ) {
    cerr << "define_svec -- svec counter overrun\n";
    exit( 1 );
  }
  
  dic_item e_item;
  if( ( e_item = svec_dict.lookup( e_str ) ) != nil ) {
    return svec_map.key( svec_dict.inf( e_item ) );
  }

  list<dc_symbol_tag> *symbol_list = new list<dc_symbol_tag>;
  dc_svec_tag e_tag = svec_num++;
  svec_dict.insert( e_str, svec_map.insert( e_tag, symbol_list ) );
  svec_rdict.insert( e_tag, e_str );
  return e_tag;
}

dc_svec_tag dc_symbol_block::lookup_svec( cstring e_str ) const {
  dic_item e_item;
  if( ( e_item = svec_dict.lookup( e_str ) ) != nil ) {
    return svec_map.key( svec_dict.inf( e_item ) );
  }
  return undef_svec;
}

bool dc_symbol_block::add_symbol( cstring symbol, cstring svec ) {
  dc_symbol_tag s_tag = lookup_symbol( symbol );
  if( s_tag != undef_symbol ) {
    dc_svec_tag e_tag = lookup_symbol( svec );
    if( e_tag != undef_svec ) {
      return add_symbol( s_tag, e_tag );
    }
  }
  return true;
}

string dc_symbol_block::svec_string( dc_svec_tag svec ) const {
  dic_item di = svec_rdict.lookup( svec );
  if( !di ) return string( "<nil>" );
  return sb.svec_rdict.inf( di );
}

bool dc_symbol_block::add_symbol( dc_symbol_tag s_tag, dc_svec_tag e_tag ) {
  list<dc_symbol_tag> *symbol_list;
  dic_item e_item  = svec_map.lookup( e_tag );
  if( !e_item ) return true;
  symbol_list = svec_map.inf( e_item );
  if( symbol_list->rank( s_tag ) ) return true;
  symbol_list->append( s_tag );
  return false;
}

dc_symbol_block::~dc_symbol_block( void ) {
  dic_item e_item;
  forall_items( e_item, svec_map ) {
    delete( svec_map.inf( e_item ) );
  }
  symbol_dict.clear();
  symbol_rdict.clear();
  svec_dict.clear();
  svec_map.clear();
  svec_rdict.clear();
}

dc_symbol::dc_symbol( void ) {
  val = undef_symbol;
  etype = undef_svec;
  rank = 0;
}

dc_symbol::dc_symbol( dc_symbol_tag s_tag, dc_svec_tag e_tag = undef_svec ) {
  if( set( s_tag, e_tag ) ) {
    val = undef_symbol;
    etype = undef_svec;
    rank = 0;
  }
}

bool dc_symbol::set( dc_symbol_tag s_tag ) {
  if( etype != undef_svec ) {
    dic_item svec_item = sb.svec_map.lookup( etype );
    if( svec_item != nil ) {
      list<dc_symbol_tag> *svec_list = sb.svec_map.inf( svec_item );
      int tmp_rank;
      if( ( tmp_rank = svec_list->rank( s_tag ) ) != 0 ) {
	val = s_tag;
	rank = tmp_rank;
	return false;
      }
    }
  } else {
    if( sb.symbol_rdict.lookup( s_tag ) != nil ) {
      val = s_tag;
      etype = undef_svec;
      rank = 0;
      return false;
    }
  }
  return true;
}

bool dc_symbol::set( dc_symbol_tag s_tag, dc_svec_tag e_tag ) {
  if( e_tag != undef_svec ) {
    dic_item svec_item = sb.svec_map.lookup( e_tag );
    if( svec_item != nil ) {
      list<dc_symbol_tag> *svec_list = sb.svec_map.inf( svec_item );
      int tmp_rank;
      if( ( tmp_rank = svec_list->rank( s_tag ) ) != 0 ) {
	val = s_tag;
	etype = e_tag;
	rank = tmp_rank;
	return false;
      }
    }
  } else {
    if( sb.symbol_rdict.lookup( s_tag ) != nil ) {
      val = s_tag;
      etype = undef_svec;
      rank = 0;
      return false;
    }
  }
  return true;
}

bool dc_symbol::set( cstring s_str, cstring e_str ) {
  dic_item symbol_item = sb.symbol_dict.lookup( s_str );
  if( !symbol_item ) return true;
  dc_symbol_tag s_tag = sb.symbol_dict.inf( symbol_item );
  dic_item svec_item = sb.svec_dict.lookup( e_str );
  if( !svec_item ) return true;
  dc_svec_tag e_tag = sb.svec_map.key( svec_item );
  list<dc_symbol_tag> *svec_list = sb.svec_map.inf( svec_item );
  int tmp_rank;
  if( ( tmp_rank = svec_list->rank( s_tag ) ) != 0 ) {
    val = s_tag;
    etype = e_tag;
    rank = tmp_rank;
    return false;
  } 
  return true;
}

void dc_symbol::set( const dc_symbol &S ) {
  val = S.val;
  etype = S.etype;
  rank = S.rank;
}

string dc_symbol::type_string( void ) const {
  string type_str;
  if( etype != undef_svec ) {
    return sb.svec_rdict.access( etype ) + " symbol";
  } else {
    return "symbol";
  }
}

ostream &dc_symbol::display_c( ostream &stream = cout ) const {
  if( val == undef_symbol ) {
    return stream << "<nil>";
  }
  return stream << sb.symbol_rdict.access( val );
}

dc_label *dc_symbol::duplicate( dc_node *parent ) const {
  dc_symbol *dupe = new dc_symbol( val, etype );
  if( !is_temporary() ) {
    if( dupe->set_both( local_label(), parent ) ) {
      delete( dupe );
      return nil;
    }
  }
  dupe->set_visible( is_visible() );
  return ( dc_label * )dupe;
}
