/*
  Copyright 2003 Ronald S. Burkey <info@sandroid.org>
  
  This file is part of yaAGC.

  yaAGC is free software; you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation; either version 2 of the License, or
  (at your option) any later version.

  yaAGC is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with yaAGC; if not, write to the Free Software
  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

  Filename:	SymbolTable.c
  Purpose:	Stuff for managing the assembler's symbol table.
  Mode:		04/11/03 RSB.	Began.
  		04/17/03 RSB.	Removed Namespaces.
		
  Concerning the concept of a symbol's namespace.  I had originally 
  intended to implement this, and so many functions had a namespace
  parameter.  I've decided to remove the parameters, but there is 
  still the underlying code for it, in case it might be handy in the
  future.
*/

#include "yaYUL.h"
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

//-------------------------------------------------------------------------
// Some global data.

// On the first pass, the symbols are collected into an unsorted array.  
// At the end of the pass the array is sorted, and duplicates will cause 
// error messages.  The symbol table is initially empty.  Whenever more 
// symbols are defined than the table has room for, its space is enlarged.  
// On the second pass, true values are assigned to the symbols.
Symbol_t *SymbolTable = NULL;
int SymbolTableSize = 0, SymbolTableMax = 0;

//-------------------------------------------------------------------------
// Delete the symbol table.

void
ClearSymbols (void)
{
  if (SymbolTable != NULL)
    free (SymbolTable);
  SymbolTable = NULL;
  SymbolTableSize = SymbolTableMax = 0;  
}

//-------------------------------------------------------------------------
// Add a symbol to the table.  The newly-added symbol always has the value
// ILLEGAL_SYMBOL_VALUE.  Returns 0 on success, or non-zero on fatal
// error.

int
AddSymbol (const char *Name)
{
  char Namespace = 0;
  
  // A sanity clause.
  if (strlen (Name) > MAX_LABEL_LENGTH)
    {
      printf ("Symbol name \"%s\" is too long.\n", Name);
      return (1);
    }
  // If the symbol table is too small, enlarge it.
  if (SymbolTableSize == SymbolTableMax)
    {
      if (SymbolTable==NULL)
        {
	  // This default size comes from the fact that I know there are about
	  // 7100 symbols in the Luminary131 symbol table.
	  SymbolTableMax = 10000;
          SymbolTable = (Symbol_t *) calloc (SymbolTableMax, sizeof (Symbol_t));
	}
      else
        {
          SymbolTableMax += 1000;
	  SymbolTable = (Symbol_t *) realloc (SymbolTable, 
					      SymbolTableMax * sizeof (Symbol_t));			   
	}
      if (SymbolTable == NULL)
        {
	  printf ("Out of memory (3).\n");
	  return (1);
	}
    }
  // Now add the symbol.
  SymbolTable[SymbolTableSize].Namespace = Namespace;
  SymbolTable[SymbolTableSize].Value.Invalid = 1;
  strcpy (SymbolTable[SymbolTableSize].Name, Name);
  SymbolTableSize++;
  return (0); 
}

//-------------------------------------------------------------------------
// Assign a symbol a new value.  Returns 0 on success.

int
EditSymbol (const char *Name, Address_t *Value)
{
  char Namespace = 0;
  Symbol_t *Symbol;
  // Find out where the symbol is located in the symbol table.
  Symbol = GetSymbol (Name);
  if (Symbol == NULL)
    {
      printf ("Implementation error: symbol %d,\"%s\" lost between passes.\n",
              Namespace, Name);
      return (1);
    }
  // This can't happen, but still ...
  if (strcmp (Name, Symbol->Name))
    {
      printf ("***** Name mismatch:  %s/%s\n", Name, Symbol->Name);
    }  
  // Check to see if the symbol is in a namespace that allows it to be
  // reassigned.
  if (0)
    {
      printf ("Symbol \"%s\" in namespace %d cannot be reassigned.\n",
              Symbol->Name, Symbol->Namespace);
    }
  // Reassign the value.
  Symbol->Value = *Value;
  return (0);
}

//-------------------------------------------------------------------------
// Compare two symbol-table entries, for comparison purposes.  Both the
// Namespace and Name fields are used.

static
int Compare (const void *Raw1, const void *Raw2)
{
#define Element1 ((Symbol_t *) Raw1)
#define Element2 ((Symbol_t *) Raw2)
  if (Element1->Namespace < Element2->Namespace)
    return (-1);
  if (Element1->Namespace > Element2->Namespace)
    return (1);
  return (strcmp (Element1->Name, Element2->Name));    
#undef Element1
#undef Element2
}

//-------------------------------------------------------------------------
// Sort the symbol table.  Returns the number of duplicated symbols.

int
SortSymbols (void)
{
  int i, j, ErrorCount = 0;
  qsort (SymbolTable, SymbolTableSize, sizeof (Symbol_t), Compare);
  // If a symbol is duplicated (in the same namespace), be remove the
  // duplicates.
  for (i = 1; i < SymbolTableSize; )
    {
      if (SymbolTable[i - 1].Namespace == SymbolTable[i].Namespace &&
          !strcmp (SymbolTable[i - 1].Name, SymbolTable[i].Name))
        {
	  printf ("Symbol \"%s\" (%d) is duplicated.\n", 
	          SymbolTable[i].Name, SymbolTable[i].Namespace);
          ErrorCount++;		  
	  for (j = i; j < SymbolTableSize; j++)
	    SymbolTable[j - 1] = SymbolTable[j];
	  SymbolTableSize--;  
	}	  
      else
        i++;	
    }
  return (ErrorCount);
}

//-------------------------------------------------------------------------
// Locate a string in the symbol table.  
// Returns a pointer to the symbol-table entry, or NULL if not found.. 

Symbol_t *
GetSymbol (const char *Name)
{
  char Namespace = 0;
  Symbol_t Symbol;
  if (strlen (Name) > MAX_LABEL_LENGTH)
    return (NULL);
  Symbol.Namespace = Namespace;
  strcpy (Symbol.Name, Name);
  return ((Symbol_t *) bsearch (&Symbol, SymbolTable, SymbolTableSize,
                                sizeof (Symbol_t), Compare));
}

//------------------------------------------------------------------------
// Print the symbol table.

void
PrintSymbolsToFile (FILE *fp)
{
  int i;
  fprintf (fp, "Symbol Table\n"
          "------------\n");
  for (i = 0; i < SymbolTableSize; i++)
    {
      if (!(i & 3) && i != 0)
        fprintf (fp, "\n");
      fprintf (fp, "%6d:   %-*s   ", i + 1, MAX_LABEL_LENGTH, SymbolTable[i].Name);
      AddressPrint (&SymbolTable[i].Value);
      if (3 != (i & 3))
        fprintf (fp, "\t\t");
    }
  fprintf (fp, "\n");  
}

void
PrintSymbols (void)
{
  PrintSymbolsToFile (stdout);
}

//------------------------------------------------------------------------
// Counts the number of unresolved symbols.

int
UnresolvedSymbols (void)
{
  int i, Ret = 0;
  for (i = 0; i < SymbolTableSize; i++)
    if (SymbolTable[i].Value.Invalid)
      Ret++;
  return (Ret);    
}

