/*
**      symtab.c - Symbol table routines
**
**
** Copyright (c) 1996,97  Hughes Technologies Pty Ltd
**
** Permission to use, copy, and distribute for non-commercial purposes,
** is hereby granted without fee, providing that the above copyright
** notice appear in all copies and that both the copyright notice and this
** permission notice appear in supporting documentation.
**
** This software is provided "as is" without any expressed or implied warranty.
**
**
*/


#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include "y.tab.h"
#include "lite.h"



typedef struct _sstack {
	sym_t	**head;
	struct	_sstack *next;
} sstk_t;


sym_t	**symHead,
	**globalHead,
	*litHead[SYM_HASH_SIZE],
	*macroHead[SYM_HASH_SIZE];
sstk_t	*symStack = NULL;

sym_t	*curArrayLiteral;
int	curArrayElement;

static	char errBuf[100];


#define	clearSymbol(sym) \
	*(sym->name) = *(sym->valBuf) = 0;		\
	sym->val = NULL;				\
	sym->type = 0;					\
	sym->array = sym->usign = 0;			\
	sym->length = sym->source = 0;			\
	sym->next = NULL;


int calculateHash(str, hashSize)
	char	*str;
	int	hashSize;
{
	register char *cp;
	register int hash;

	hash = 0;
	cp = str;
	if (*cp == '$' || *cp == '@')
		cp++;
	while(*cp != 0)
	{
		hash += *cp++;
	}
	hash = hash % hashSize;
	return(hash);
}

void initSymbolTables()
{
	int	loop;

	symHead = (sym_t **)malloc(SYM_HASH_SIZE * sizeof(sym_t *));
	globalHead = symHead;
	for (loop = 0; loop < SYM_HASH_SIZE; loop++)
	{
		symHead[loop] = NULL;
		litHead[loop] = NULL;
		macroHead[loop] = NULL;
	}
}



void symStoreValue(sym,val,len,type)
	sym_t	*sym;
	char	*val;
	int	len,
		type;
{
	switch(type)
	{
		case TYPE_CHAR:
			if (len >= VAL_BUF_LEN)
			{
				sym->val = malloc(len + 1);
				strcpy(sym->val, val);
			}
			else
			{
				strcpy(sym->valBuf, val);
				sym->val = sym->valBuf;
			}
			break;

		default:
			if (len > VAL_BUF_LEN)
			{
				sym->val = malloc(len);
				bcopy(val, sym->val, len);
			}
			else
			{
				bcopy(val, sym->valBuf, len);
				sym->val = sym->valBuf;
			}
			break;
	}
}




void symStackIn()
{
	sstk_t	*new;

	new = (sstk_t *)malloc(sizeof(sstk_t));
	new->head = symHead;
	new->next = symStack;
	symStack = new;
	symHead = (sym_t **)malloc(SYM_HASH_SIZE * sizeof(sym_t *));
	bzero(symHead, SYM_HASH_SIZE * sizeof(sym_t *));
}


void symStackOut()
{
	sstk_t	*tmp;
	sym_t	*cur, *prev, **head;
	int	count;

	tmp = symStack;
	head = symHead;
	symStack = symStack->next;
	symHead = tmp->head;
	free(tmp);

	for (count = 0; count < SYM_HASH_SIZE; count++)
	{
		prev = NULL;
		cur = head[count];
		while(cur)
		{
			prev = cur;
			cur = cur->next;
			symFreeSymbol(prev);
		}
	}
	free(head);
}



#define myStrcmp(s1,s2,res) \
	{ 					\
		register char *cp1=s1, *cp2=s2; \
		while(*cp1 && *cp2) 		\
		{ 				\
			if (*cp1 != *cp2) 	\
			{			\
				res=-1;		\
				break;		\
			} 			\
			cp1++; cp2++; 		\
		}				\
		if (*cp1 != *cp2) 		\
			res = -1; 		\
		else 				\
			res = 0;		\
	}


/**************************************************************************
** 	Clean-up Routines
*/

void symClearArray(sym)
	sym_t	*sym;
{
	array_t	*cur,
		*prev;

	cur = (array_t *)sym->val;
	while(cur)
	{
		prev = cur;
		cur = cur->next;
		symFreeSymbol(prev->sym);
		free(prev);
	}
}


void symFreeSymbolData(sym)
	sym_t	*sym;
{
	if (!sym)
	{
		return;
	}
	if (!sym->val)
	{
		return;
	}
	if (sym->array == ARRAY)
	{
		symClearArray(sym);
	}
	else
	{
		if (sym->val != sym->valBuf)
			free(sym->val);
	}
}





/**************************************************************************
** 	Symbol table management routines
*/


void symStoreSymbol(head, sym)
	sym_t	**head,
		*sym;
{
	int	hash;

	hash = calculateHash(sym->name, SYM_HASH_SIZE);
	sym->next = head[hash];
	head[hash] = sym;
}



sym_t *symCreateSymbol(name,type,array)
	char	*name;
	int	type,
		array;
{
	sym_t	*new;
	array_t	*val;

	new = (sym_t *)malloc(sizeof(sym_t));
	if (!new)
	{
		runError("Out of memory!");
		exit(1);
	}
	clearSymbol(new);
	strcpy(new->name,name);
	new->type = type;
	new->array = array;
	if (array == ARRAY)
	{
		new->val = (char *)malloc(sizeof(array_t));
		val = (array_t *)new->val;
		val->sym = (sym_t *)malloc(sizeof(sym_t));
		clearSymbol(val->sym);
		val->next = NULL;
	}
	else
	{
		new->val = NULL;
	}
	if (*name == '$')
	{
		symStoreSymbol(symHead, new);
	}
	else
	{
		*(new->name) = '$';
		symStoreSymbol(globalHead, new);
	}

#ifdef SYM_TORTURE
	symCheckSymbol(NULL);
#endif
	return(new);
}



sym_t *symGetEnvironVar(name)
	char	*name;
{
	char	*val;
	sym_t	*sym;

	val = (char *)getenv(name+1);
	if (!val)
		return(NULL);
	sym = symCreateSymbol(name, TYPE_CHAR, SCALAR);
	sym->length = strlen(val) + 1;
	symStoreValue(sym,val,sym->length,TYPE_CHAR);
	return(sym);
}


int symCheckSymbol(name)
	char	*name;
{
	sym_t	*sym;
	int	res,
		hash;

	hash = calculateHash(name,SYM_HASH_SIZE);
	if (*name == '$')
		sym = symHead[hash];
	else
		sym = globalHead[hash];
	while (sym)
	{
		if (name)
		{
			if (*(name+1) == *(sym->name+1))
			{
				myStrcmp(name+1,sym->name+1,res);
				if (res == 0)
				{
					return(1);
				}
			}
		}
		sym = sym->next;
	}
	return(0);
}




sym_t *symGetSymbol(name)
	char	*name;
{
	register sym_t	*sym;
	int	hash;

	/*
	** Lookup the local symbol table
	*/
	hash = calculateHash(name,SYM_HASH_SIZE);
	if (*name == '$')
	{
		sym = symHead[hash];
	}
	else
	{
		if (*name == '@')
		{
			sym = globalHead[hash];
		}
		else
		{
			sym = litHead[hash];
		}
	}
	while (sym)
	{
		if (*name == '$' || *name == '@')
		{
			if (*(name+1) != *(sym->name+1) || 
			    *(name+2) != *(sym->name+2))
			{
				sym = sym->next;
				continue;
			}
			if (strcmp(name+1,sym->name+1) == 0)
			{
				return(sym);
			}
		}
		else
		{
			if (*name != *(sym->name) || 
			    *(name+1) != *(sym->name+1))
			{
				sym = sym->next;
				continue;
			}
			if (strcmp(name,sym->name) == 0)
			{
				return(sym);
			}
		}
		sym = sym->next;
	}

	/*
	** Last resort - go for an environment variable
	*/
	return(symGetEnvironVar(name));
}



/**************************************************************************
** 	Symbol value management routines
*/


void symSetValue(dst, src)
	sym_t	*dst,
		*src;
{
	symFreeSymbolData(dst);
	if (src->array == ARRAY)
	{
		dst->val = (char *)symArrayDup(src);
	}
	else
	{
        	symStoreValue(dst,src->val,src->length,src->type);
	}
	dst->type = src->type;
	dst->length = src->length;
	dst->array = src->array;
#ifdef SYM_TORTURE
	symCheckSymbol(NULL);
#endif
}




#define	MAX_FREE_SYMS	20
static int numFreeSyms = 0;
static sym_t *freeSyms;


void symFreeSymbol(sym)
	sym_t	*sym;
{
	symFreeSymbolData(sym);
	*(sym->name) = 0;
	if (numFreeSyms < MAX_FREE_SYMS)
	{
		sym->next = freeSyms;
		freeSyms = sym;
		numFreeSyms++;
	}
	else
	{
		free(sym);
	}
}


sym_t *symdup(src)
	sym_t	*src;
{
	sym_t	*dst;

	if (!src)
		return(NULL);
	if (numFreeSyms > 0)
	{
		numFreeSyms--;
		dst = freeSyms;
		freeSyms = freeSyms->next;
	}
	else
	{
		dst = (sym_t *)malloc(sizeof(sym_t));
	}
	if (!dst)
	{
		runError("Out of memory!");
		exit(1);
	}
	dst->val = NULL;
	dst->type = dst->array = dst->length = dst->source = 0;
	dst->next = NULL;
	strcpy(dst->name,src->name);
	dst->type = src->type;
	dst->array = src->array;
	dst->length = src->length;
	if (dst->array == SCALAR)
	{
                symStoreValue(dst,src->val,src->length,src->type);
	}
	else
	{
		dst->val = (char *)symArrayDup(src);
	}
#ifdef SYM_TORTURE
	symCheckSymbol(NULL);
#endif
	return(dst);
}


void symTypeCast(sym,typeIndex)
	sym_t	*sym;
	int	typeIndex;
{
	type_t	*type;
	char	*buf,
		lit;
	int	length;

	/*
	** Unpack the current value into external form
	*/
	if (sym->array != SCALAR)
	{
		runError("Can't cast an array to a scalar type");
		exit(1);
	}
	lit = 0;
	if (*(sym->name))
	{
		if (*(sym->name) != '$' && *(sym->name) != '@')
			lit = 1;
	}
	type = typeGetType(sym->type);
	if (! type)
	{
		runError("Unknown type!");
		exit(1);
	}
	length = (*type->unpack)(&buf,sym->val,sym->length);


	/*
	** Repack the value into the internal form of the new type
	*/
	type = typeGetType(typeIndex);
	if (! type)
	{
		runError("Unknown type!");
		exit(1);
	}
	symFreeSymbolData(sym);
	sym->length = (*type->pack)(buf,&(sym->val),length, lit);
	if (sym->length < 0)
	{
		runError("Invalid type for cast");
		exit(1);
	}
	sym->type = typeIndex;
#ifdef SYM_TORTURE
	symCheckSymbol(NULL);
#endif
	free(buf);
}


void symCreateIntGlobal(name,val)
	char	*name;
	int	val;
{
	sym_t	*new;

	new = symCreateSymbol(name,TYPE_INT,SCALAR);
        new->length = 4;
        bcopy(&val,new->valBuf,4);
	new->val = new->valBuf;
}

void symCreateRealGlobal(name,val)
	char	*name;
	double	val;
{
	sym_t	*new;

	new = symCreateSymbol(name,TYPE_REAL,SCALAR);
        new->length = 8;
        bcopy(&val,new->valBuf,8);
	new->val = new->valBuf;
}


void  symCreateCharGlobal(name,val)
	char	*name;
	char	*val;
{
	sym_t	*new;

	new = symCreateSymbol(name,TYPE_CHAR,SCALAR);
	symStoreValue(new,val,strlen(val),TYPE_CHAR);
        new->length = strlen(val)+1;
}


void symCreateArrayGlobal(name,val, num)
	char	*name;
	char	*val[];
	int	num;
{
	sym_t	*new,
		*cur;
	int	count;

	new = symCreateSymbol(name,TYPE_CHAR,ARRAY);
	for (count = 0; count < num; count++)
	{
		cur = createCharSymbol(val[count]);
		symSetArrayElement(new,count,cur);
	}
}



/**************************************************************************
** 	Array management routines
*/


sym_t *symGetArrayElement(sym, index)
	sym_t	*sym;
	int	index;
{
	array_t	*cur;

	if (!sym)
		return(NULL);
	if (sym->array != ARRAY)
		return(NULL);
	cur = (array_t *)sym->val;
	while(cur)
	{
		if (index == 0)
			return(cur->sym);
		index--;
		cur = cur->next;
	}
	return(NULL);
}




void symSetArrayElement(sym, index, val)
	sym_t	*sym;
	int	index;
	sym_t	*val;
{
	array_t	*cur;

	cur = (array_t *)sym->val;
	while(index > 0)
	{
		if (cur->next == NULL)
		{
			cur->next = (array_t *)malloc(sizeof(array_t));
			bzero(cur->next,sizeof(array_t));
			cur->next->sym = (sym_t *)malloc(sizeof(sym_t));
			clearSymbol(cur->next->sym);
			cur->next->next = NULL;
		}
		cur = cur->next;
		index--;
	}
	symFreeSymbol(cur->sym);
	cur->sym = val;
#ifdef SYM_TORTURE
	symCheckSymbol(NULL);
#endif
}


int symGetNumArrayElements(sym)
	sym_t	*sym;
{
	int	count=0;
	array_t	*cur;

	cur = (array_t *)sym->val;
	while(cur)
	{
		cur = cur->next;
		count++;
	}
	return(count);
}


array_t *symArrayDup(src)
	sym_t	*src;
{
	array_t	*head = NULL,
		*tail = NULL,
		*new,
		*cur;

	cur = (array_t *)src->val;
	while(cur)
	{
		new = (array_t *)malloc(sizeof(array_t));
		bzero(new,sizeof(array_t));
		if (!new)
		{
			runError("Out of memory!");
			exit(1);
		}
		new->sym = symdup(cur->sym);
		new->next = NULL;
		if (!head)
		{
			head = new;
		}
		else
		{
			tail->next = new;
		}
		tail = new;
		cur = cur->next;
	}
#ifdef SYM_TORTURE
	symCheckSymbol(NULL);
#endif
	return(head);
}



void symAddArrayElement(textVal)
	char	*textVal;
{
	sym_t	*element;
	char	*cp;

	cp = textVal;
	if (*cp == '"')
	{
		cp++;
		*(textVal + strlen(textVal) -1) = 0;
	}
	element = createCharSymbol(expandText(cp));
	symSetArrayElement(curArrayLiteral,curArrayElement,element);
	curArrayElement++;
}


/**************************************************************************
** 	Literal symbol management routines
*/

int curLitIdx = 0;


sym_t *symCreateLiteral(val, hint)
	char	*val;
	int	hint;
{
	sym_t	*sym, *cur;
	char	nameBuf[10];
	int	hash, count;
	type_t	*type;

	/*
	** Create the literal symbol
	*/
	sym = (sym_t*)malloc(sizeof(sym_t));
	if (!sym)
	{
		runError("Out of memory!");
		exit(1);
	}
	clearSymbol(sym);

	/*
	** Give it a unique name.  Literals don't have a $ in their name.
	** This ensures there'll be no clash with a user defined variable.
	*/
	curLitIdx++;
	sprintf(sym->name,"%d",curLitIdx);
	hash = calculateHash(sym->name, SYM_HASH_SIZE);

	/*
	** Determine the type and create the internal representation
	** of the value.
	*/
	if (hint == NUM)
	{
		sym->type = typeDetermineType(val);
		if (sym->type < 0)
		{
			runError("Can't determine type for literal value");
			exit(1);
		}
	}
	else
	{
		sym->type = TYPE_CHAR;
	}
	type = typeGetType(sym->type);
	sym->length = (*type->pack)(val,&(sym->val),strlen(val), 1);
	sym->usign = 0;
	sym->array = SCALAR;

	/*
	** Can we use an existing literal rather than creating a new one?
	*/
	for (count=0; count < SYM_HASH_SIZE; count++)
	{
		cur = litHead[count];
		while(cur)
		{
			if (cur->type != sym->type || 
				cur->length != sym->length || 
				cur->array != sym->array)
			{
				cur = cur->next;
				continue;
			}
			if (bcmp(cur->val,sym->val,cur->length) != 0)
			{
				cur = cur->next;
				continue;
			}
			break;
		}
		if (cur)
		{
			symFreeSymbol(sym);
			curLitIdx--;
			return(cur);
		}
	}

	/*
	** Create a new one
	*/
	sym->next = litHead[hash];
	litHead[hash] = sym;
#ifdef SYM_TORTURE
	symCheckSymbol(NULL);
#endif
	return(sym);
}


char *symUnpackSymbol(sym)
	sym_t	*sym;
{
	char	*output;
	type_t	*type;
	static	char blank[] = "";

	if (!sym)
		return(blank);	
	if (!sym->val)
		return(blank);	
	type = typeGetType(sym->type);
	if (!type)
	{
		runError("Unknown type! (internal error)");
		exit(1);
	}
	(*type->unpack)(&output,sym->val,sym->length);
	return(output);
}



/**************************************************************************
** 	Convenience functions
*/

sym_t *createIntSymbol(val)
	int	val;
{
	sym_t 	*new;

	new = (sym_t *)malloc(sizeof(sym_t));
	if (!new)
	{
		runError("Out of memory!");
		exit(1);
	}
	clearSymbol(new);
	*(new->name) = 0;
	new->type = TYPE_INT;
	new->length = 4;
	new->array = SCALAR;
	new->val = (char *)malloc(4);
	bcopy4(&val, new->val);
	return(new);
}


sym_t *createUintSymbol(val)
	int	val;
{
	sym_t 	*new;

	new = (sym_t *)malloc(sizeof(sym_t));
	if (!new)
	{
		runError("Out of memory!");
		exit(1);
	}
	clearSymbol(new);
	*(new->name) = 0;
	new->type = TYPE_UINT;
	new->length = 4;
	new->array = SCALAR;
	new->val = (char *)malloc(4);
	bcopy4(&val, new->val);
	return(new);
}


sym_t *createRealSymbol(val)
	double	val;
{
	sym_t 	*new;

	new = (sym_t *)malloc(sizeof(sym_t));
	if (!new)
	{
		runError("Out of memory!");
		exit(1);
	}
	clearSymbol(new);
	*(new->name) = 0;
	new->type = TYPE_REAL;
	new->length = 8;
	new->array = SCALAR;
	new->val = (char *)malloc(8);
	bcopy8(&val, new->val);
	return(new);
}



sym_t *createCharSymbol(val)
	char	*val;
{
	sym_t 	*new;

	new = (sym_t *)malloc(sizeof(sym_t));
	if (!new)
	{
		runError("Out of memory!");
		exit(1);
	}
	clearSymbol(new);
	*(new->name) = 0;
	new->type = TYPE_CHAR;
	new->array = SCALAR;
	if (val)
		new->val = (char *)strdup(val);
	else
		new->val = (char *)strdup("");
	new->length = strlen(new->val) + 1;
	return(new);
}


sym_t *createArray()
{
	sym_t 	*new;
	array_t	*val;

	new = (sym_t *)malloc(sizeof(sym_t));
	if (!new)
	{
		runError("Out of memory!");
		exit(1);
	}
	clearSymbol(new);
	*(new->name) = 0;
	new->type = TYPE_CHAR;
	new->length = 0;
	new->array = ARRAY;
	new->val = (char *)malloc(sizeof(array_t));
	bzero(new->val,sizeof(array_t));
	val = (array_t *)new->val;
	val->sym = (sym_t *)malloc(sizeof(sym_t));
	clearSymbol(val->sym);
	return(new);
}

sym_t *createArrayLiteral()
{
	sym_t	*array;
	int	hash;

	array = createArray();
	curLitIdx++;
        sprintf(array->name,"%d",curLitIdx);

	hash = calculateHash(array->name,SYM_HASH_SIZE);
	array->next = litHead[hash];
	litHead[hash] = array;
	return(array);
}



/*
** Macro handling routines
*/


sym_t *symGetMacro(macro)
	char	*macro;
{
	sym_t	*cur;
	int	hash;

	hash = calculateHash(macro,SYM_HASH_SIZE);
	cur = macroHead[hash];
	while(cur)
	{
		if (strcmp(cur->name,macro) == 0)
		{
			return(cur);
		}
		cur = cur->next;
	}
	return(NULL);
}



void symCreateCharMacro(macro,val)
	char	*macro;
	char	*val;
{
	sym_t	*new;
	int	hash;

	hash = calculateHash(macro,SYM_HASH_SIZE);
	new = symGetMacro(macro);
	if (new)
	{
		sprintf(errBuf, "Macro load of '%s' failed.  Non-unique name.",
			macro);
		runError(errBuf);
		exit(1);
	}

	new = createCharSymbol(val);
	new->array = SCALAR;
	strcpy(new->name,macro);
	new->next = macroHead[hash];
	macroHead[hash] = new;
}


void symCreateIntMacro(macro,val)
	char	*macro;
	int	val;
{
	sym_t	*new;
	int	hash;

	hash = calculateHash(macro,SYM_HASH_SIZE);
	new = symGetMacro(macro);
	if (new)
	{
		sprintf(errBuf, "Macro load of '%s' failed.  Non-unique name.",
			macro);
		runError(errBuf);
		exit(1);
	}

	new = createIntSymbol(val);
	new->array = SCALAR;
	strcpy(new->name,macro);
	new->next = macroHead[hash];
	macroHead[hash] = new;
}


void symCreateRealMacro(macro,val)
	char	*macro;
	double	val;
{
	sym_t	*new;
	int	hash;

	hash = calculateHash(macro,SYM_HASH_SIZE);
	new = symGetMacro(macro);
	if (new)
	{
		sprintf(errBuf, "Macro load of '%s' failed.  Non-unique name.",
			macro);
		runError(errBuf);
		exit(1);
	}

	new = createRealSymbol(val);
	new->array = SCALAR;
	strcpy(new->name,macro);
	new->next = macroHead[hash];
	macroHead[hash] = new;
}



sym_t *symCreateMacroLiteral(macro)
	char	*macro;
{
	sym_t	*sym,
		*cur,
		*new;
	int	hash,
		count;

	sym = symGetMacro(macro);
	if (!sym)
	{
		sprintf(errBuf,"Unknown macro '%s'!",macro);
		parseError(errBuf);
		exit(1);
	}

	/*
	** Can we use an existing literal rather than creating a new one?
	*/
	for (count=0; count < SYM_HASH_SIZE; count++)
	{
		cur = litHead[count];
		while(cur)
		{
			if (cur->type != sym->type || 
				cur->length != sym->length || 
				cur->array != sym->array)
			{
				cur = cur->next;
				continue;
			}
			if (bcmp(cur->val,sym->val,cur->length) != 0)
			{
				cur = cur->next;
				continue;
			}
			break;
		}
		if (cur)
		{
			return(cur);
		}
	}

	/* Nope.  Create it */
	new = symdup(sym);
        curLitIdx++;
        sprintf(new->name,"%d",curLitIdx);
	hash = calculateHash(new->name,SYM_HASH_SIZE);
	new->next = litHead[hash];
	litHead[hash] = new;
	return(new);
}
