/* SymbolTable.c */
/*****************************************************************************/
/*                                                                           */
/*    Out Of Phase:  Digital Music Synthesis on General Purpose Computers    */
/*    Copyright (C) 1994  Thomas R. Lawrence                                 */
/*                                                                           */
/*    This program 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.                                    */
/*                                                                           */
/*    This program 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 this program; if not, write to the Free Software            */
/*    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.              */
/*                                                                           */
/*    Thomas R. Lawrence can be reached at tomlaw@world.std.com.             */
/*                                                                           */
/*****************************************************************************/

#include "MiscInfo.h"
#include "Audit.h"
#include "Debug.h"
#include "Definitions.h"

#include "SymbolTable.h"
#include "SymbolTableEntry.h"
#include "TrashTracker.h"
#include "Memory.h"
#include "DataMunging.h"


/* since we use bit masking, the hash table size must be a power of 2 */
#define HASHTABLESIZE (1L << 10)
#define HASHTABLEMASK (HASHTABLESIZE - 1)


typedef struct SymbolHeadRec
	{
		long										LexicalLevel;
		struct SymbolHeadRec**	PreviousMetaAddress;
		struct SymbolHeadRec*		Next; /* hash bucket link; also used on free list */
		SymbolRec*							Symbol;
		struct SymbolHeadRec*		LexicalLevelNext;
	} SymbolHeadRec;


typedef struct LevelRec
	{
		struct LevelRec*				OneScopeDown; /* also used on free list */
		SymbolHeadRec*					ThisLevelList;
	} LevelRec;


struct SymbolTableRec
	{
		long								LexicalLevel;
		LevelRec*						LevelStack;
		TrashTrackRec*			Allocator;

		LevelRec*						FreeLevelList;
		SymbolHeadRec*			FreeSymbolHeadList;

		SymbolHeadRec*			HashTable[HASHTABLESIZE];
	};


/* create a new symbol table */
SymbolTableRec*			NewSymbolTable(struct TrashTrackRec* TrashTracker)
	{
		SymbolTableRec*		SymbolTable;
		long							Scan;

		SymbolTable = (SymbolTableRec*)AllocTrackedBlock(sizeof(SymbolTableRec),TrashTracker);
		if (SymbolTable == NIL)
			{
				return NIL;
			}
		SetTag(SymbolTable,"SymbolTableRec");

		for (Scan = 0; Scan < HASHTABLESIZE; Scan += 1)
			{
				SymbolTable->HashTable[Scan] = NIL;
			}

		SymbolTable->FreeLevelList = NIL;
		SymbolTable->FreeSymbolHeadList = NIL;

		SymbolTable->LexicalLevel = 0;
		SymbolTable->Allocator = TrashTracker;

		SymbolTable->LevelStack = (LevelRec*)AllocTrackedBlock(sizeof(LevelRec),TrashTracker);
		if (SymbolTable->LevelStack == NIL)
			{
				return NIL;
			}
		SetTag(SymbolTable->LevelStack,"SymbolTableRec:  LevelStackRec");

		SymbolTable->LevelStack->OneScopeDown = NIL;
		SymbolTable->LevelStack->ThisLevelList = NIL;

		return SymbolTable;
	}


/* create a new symbol table level */
MyBoolean						IncrementSymbolTableLevel(SymbolTableRec* SymbolTable)
	{
		LevelRec*					NewLevel;

		CheckPtrExistence(SymbolTable);

		if (SymbolTable->FreeLevelList != NIL)
			{
				NewLevel = SymbolTable->FreeLevelList;
				SymbolTable->FreeLevelList = SymbolTable->FreeLevelList->OneScopeDown;
			}
		 else
			{
				NewLevel = (LevelRec*)AllocTrackedBlock(sizeof(LevelRec),SymbolTable->Allocator);
				if (NewLevel == NIL)
					{
						return False;
					}
			}

		NewLevel->OneScopeDown = SymbolTable->LevelStack;
		NewLevel->ThisLevelList = NIL;

		SymbolTable->LevelStack = NewLevel;
		SymbolTable->LexicalLevel += 1;

		return True;
	}


/* drop the current symbol table lexical level */
void								DecrementSymbolTableLevel(SymbolTableRec* SymbolTable)
	{
		SymbolHeadRec*		Scan;

		CheckPtrExistence(SymbolTable);
		ERROR(SymbolTable->LexicalLevel == 0,PRERR(ForceAbort,
			"DecrementSymbolTableLevel:  at outer level -- can't drop"));

		/* this list has been built in the order symbols were added.  so when we delink */
		/* them, it's in the reverse order.  by induction, each element being deleted */
		/* is at the head of it's hash bucket.  this is why we can use the metapointer. */
		Scan = SymbolTable->LevelStack->ThisLevelList;
		while (Scan != NIL)
			{
				SymbolHeadRec*		Temp;

				/* save the first node we are dropping */
				Temp = Scan;

				/* delete node from current level's list */
				Scan = Scan->LexicalLevelNext;

				/* delink node from it's bucket */
				*(Temp->PreviousMetaAddress) = Temp->Next;

				/* stick temp onto the free list */
				Temp->Next = SymbolTable->FreeSymbolHeadList;
				SymbolTable->FreeSymbolHeadList = Temp;
			}

		/* drop the stack record */
		SymbolTable->LevelStack = SymbolTable->LevelStack->OneScopeDown;
		SymbolTable->LexicalLevel -= 1;
	}


/* add symbol table entry to the symbol table.  returns a result code */
AddSymbolType				AddSymbolToTable(SymbolTableRec* SymbolTable,
											struct SymbolRec* SymbolToAdd)
	{
		SymbolHeadRec*		SymbolHead;
		long							HashIndex;
		char*							SymbolName;

		CheckPtrExistence(SymbolTable);
		CheckPtrExistence(SymbolToAdd);

		/* find out where in the hash table it belongs */
		HashIndex = HASHTABLEMASK & GetSymbolHashValue(SymbolToAdd);

		/* see if symbol already exists */
		SymbolName = GetSymbolName(SymbolToAdd);
		CheckPtrExistence(SymbolName);
		/* it must be at this level only */
		SymbolHead = SymbolTable->HashTable[HashIndex];
		while ((SymbolHead != NIL) && (SymbolHead->LexicalLevel == SymbolTable->LexicalLevel))
			{
				char*							OtherSymbolName;

				OtherSymbolName = GetSymbolName(SymbolHead->Symbol);
				if (PtrSize(OtherSymbolName) == PtrSize(SymbolName))
					{
						if (MemEqu(OtherSymbolName,SymbolName,PtrSize(SymbolName)))
							{
								return eAddSymbolAlreadyExists;
							}
					}
				SymbolHead = SymbolHead->Next;
			}

		if (SymbolTable->FreeSymbolHeadList != NIL)
			{
				SymbolHead = SymbolTable->FreeSymbolHeadList;
				SymbolTable->FreeSymbolHeadList = SymbolTable->FreeSymbolHeadList->Next;
			}
		 else
			{
				SymbolHead = (SymbolHeadRec*)AllocTrackedBlock(sizeof(SymbolHeadRec),
					SymbolTable->Allocator);
				if (SymbolHead == NIL)
					{
						return eAddSymbolNoMemory;
					}
				SetTag(SymbolHead,"SymbolHeadRec");
			}

		/* set the meta reference for delinking */
		SymbolHead->PreviousMetaAddress = &(SymbolTable->HashTable[HashIndex]);

		/* add the thing to the hash chain (at the head) */
		SymbolHead->Next = SymbolTable->HashTable[HashIndex];
		SymbolTable->HashTable[HashIndex] = SymbolHead;

		/* put the symbol in the record */
		SymbolHead->Symbol = SymbolToAdd;

		/* link it into the lexical level thing */
		SymbolHead->LexicalLevelNext = SymbolTable->LevelStack->ThisLevelList;
		SymbolTable->LevelStack->ThisLevelList = SymbolHead;

		/* set the lexical level */
		SymbolHead->LexicalLevel = SymbolTable->LexicalLevel;

		return eAddSymbolNoErr;
	}


/* get a symbol from the symboldflksakdo table */
/* it returns NIL if the entry was not found. */
struct SymbolRec*		GetSymbolFromTable(SymbolTableRec* SymbolTable, char* NameString,
											long NameStringLength)
	{
		SymbolHeadRec*		SymbolHead;

		CheckPtrExistence(SymbolTable);

		SymbolHead = SymbolTable->HashTable[HASHTABLEMASK
			& UseSymbolTableHash(NameString,NameStringLength)];
		while (SymbolHead != NIL)
			{
				char*							OtherSymbolName;

				OtherSymbolName = GetSymbolName(SymbolHead->Symbol);
				if (PtrSize(OtherSymbolName) == NameStringLength)
					{
						if (MemEqu(OtherSymbolName,NameString,NameStringLength))
							{
								/* found it! */
								return SymbolHead->Symbol;
							}
					}
				SymbolHead = SymbolHead->Next;
			}

		return NIL;
	}
