/****************************************************************************/
/*                                                                          */
/*  Copyright (c) 1996-2002                                                 */
/*  Jeremy Levitt                                                           */
/*  All Rights Reserved                                                     */
/*                                                                          */
/*  File:  symbol.h                                                         */
/*                                                                          */
/****************************************************************************/

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

const char *EMPTY="*NULL*";
TableElem *Symbol_Table::_nullSymbol=NULL;
Symbol_Table *Symbol::_Symbol_Table;
unsigned Symbol_Init::_count;


///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// Function: Symbol_Init::Symbol_Init					     //
// Description: Initialize symbol table                                      //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////
Symbol_Init::Symbol_Init()
{
  if (_count++ == 0)
    Symbol::_Symbol_Table = new Symbol_Table(EMPTY);
}


///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// Function: Symbol_Init::~Symbol_Init					     //
// Description: Destroy symbol table                                         //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////
Symbol_Init::~Symbol_Init()
{
  if (--_count == 0) {
    delete Symbol::_Symbol_Table;
  }
}

/****************************************************************************/
/*  Symbol_Table::Symbol_Table()                                              */
/*                                                                          */
/*  Abstract:  The constructor for Symbol_Tables sets all the hash bins     */
/*             to empty and initializes the NULL symbol.                    */
/****************************************************************************/
Symbol_Table::Symbol_Table(const char* nullSymbol)
{
  for(int i=0;i<SYMBOL_TABLE_SIZE;i++) _hashEntry[i]=NULL;
  (_nullSymbol = Insert(nullSymbol))->AddReference();
}


///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// Function: Symbol_Table::~Symbol_Table					     //
// Description: Clean up symbol table                                        //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////
Symbol_Table::~Symbol_Table()
{
  for(int i=0;i<SYMBOL_TABLE_SIZE;i++) {
    while (_hashEntry[i] != NULL) {
      delete _hashEntry[i];
    }
  }
}

/****************************************************************************/
/*  Symbol_Table::HashFunction()                                             */
/*                                                                          */
/*  Abstract:  A simple function that calculates a unique index into the    */
/*             hash table based on the input string.                        */
/****************************************************************************/
int Symbol_Table::HashFunction(const char *chars)
{
  int hashIndex = 1;
  int length = strlen(chars);

  for (int i=0; i<length; i++)
    hashIndex = (hashIndex*int(chars[i])) % SYMBOL_TABLE_SIZE;

  return(hashIndex);
}


/****************************************************************************/
/*  Symbol_Table::Insert()                                                   */
/*                                                                          */
/*  Abstract:  Returns the pointer to the tableElem that contains the       */
/*             desired string;  if such a tableElem does not exist, one     */
/*             is created and linked into the Symbol_Table.                  */
/****************************************************************************/
TableElem *Symbol_Table::Insert(const char* chars)
{
  int hashIndex = HashFunction(chars);
  TableElem **temp = &_hashEntry[hashIndex];

  // Find the insertion point in the list.
  for (; (*temp != NULL); temp = &(*temp)->_next)
    if (strcmp(chars, (*temp)->_chars) >= 0) break;

  // If the string is there return its TableElem pointer.
  if (*temp && strcmp(chars, (*temp)->_chars) == 0)
    return *temp;

  // Otherwise create a new TableElem and return a pointer to it.
  else {
    (void)(new TableElem(chars, temp, *temp));
    return *temp;
  }
}


/****************************************************************************/
/*  TableElem::~TableElem()                                                 */
/*                                                                          */
/*  Abstract:  Removed this TableElem from the linked list.                 */
/****************************************************************************/
TableElem::~TableElem()
{
  *_prev = _next;
  if (_next) _next->_prev = _prev;
  delete [] _chars;
}


/****************************************************************************/
/*  Symbol::Symbol()                                                        */
/****************************************************************************/
Symbol::Symbol()
{ 
  _tableElem = _Symbol_Table->Null_Symbol();
  _tableElem->AddReference(); 
}


/****************************************************************************/
/*  Symbol::Symbol(const char *chars)                                       */
/****************************************************************************/
Symbol::Symbol(const char *chars)
{
  _tableElem = _Symbol_Table->Insert(chars);
  _tableElem->AddReference(); 
}


/****************************************************************************/
/*  Symbol::operator =                                                      */
/****************************************************************************/
Symbol& Symbol::operator= (const Symbol& rhs)
{
  if (*this != rhs) {
    _tableElem->RemoveReference();
    rhs._tableElem->AddReference();
    _tableElem = rhs._tableElem;
  }

  return(*this);
}


/****************************************************************************/
/*  TableElem::TableElem()                                                  */
/*                                                                          */
/*  Initialize the tableElem and insert it into the linked list.            */
/****************************************************************************/
TableElem::TableElem(const char* chars, TableElem** prev, TableElem* next)
: _numRefs(0), _prev(prev), _next(next)
{
  _chars = new char[strlen(chars)+1];
  strcpy(_chars, chars);
  (*_prev) = this;
  if (_next) _next->_prev = &_next;
}


/***************************************************************************/
/*  operator <<                                                            */
/***************************************************************************/
ostream& operator << (ostream& cout, const Symbol_Table& symTable)
{
  for(int i=0; i<SYMBOL_TABLE_SIZE; i++) {
    for(TableElem *temp = symTable._hashEntry[i]; temp != NULL; 
        temp = temp->_next) {
      cout << *temp;
    }
  }
  return cout;
}


/***************************************************************************/
/*  operator <<                                                            */
/***************************************************************************/
ostream& operator << (ostream& cout, const TableElem& tableElem)
{
  cout << "element=\"" << tableElem._chars << "\" ";
  cout << "id=" << (void *)&tableElem << " ";
  cout << "numRefs=" << tableElem._numRefs << endl;
  return cout;
}


/***************************************************************************/
/*  Symbol::MatchCase                                                      */
/***************************************************************************/
int Symbol::MatchCase(const Symbol x, const Symbol y)
{
  return strcasecmp(x._tableElem->Chars(), y._tableElem->Chars()) == 0;
}


/***************************************************************************/
/*  operator <<                                                            */
/***************************************************************************/
ostream& operator<<(ostream& cout, const Symbol symbol)
{
  cout << symbol.Chars();
  return cout;
}
