/*
**      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 "y.tab.h"
#include "lite.h"



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


sym_t	*symHead = NULL,
	*litHead = NULL,
	*macroHead = NULL;
sstk_t	*symStack = NULL;

sym_t	*curArrayLiteral;
int	curArrayElement;

static	char errBuf[100];


symStackIn()
{
	sstk_t	*new;

	new = (sstk_t *)malloc(sizeof(sstk_t));
	new ->head = symHead;
	new->next = symStack;
	symStack = new;
	symHead = NULL;
}


symStackOut()
{
	sstk_t	*tmp;

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



#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
	{
		free(sym->val);
	}
}





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


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

	new = (sym_t *)malloc(sizeof(sym_t));
	bzero(new,sizeof(sym_t));
	if (!new)
	{
		runError("Out of memory!");
		exit(1);
	}
	new->name = (char *)strdup(name);
	new->type = type;
	new->array = array;
	new->length = 0;
	new->source = 0;
	if (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));
		bzero(val->sym,sizeof(sym_t));
	}
	else
	{
		new->val = NULL;
	}
	new->next = symHead;
	symHead = 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->val = (char *)strdup(val);
	sym->length = strlen(val);
	return(sym);
}


int symCheckSymbol(name)
	char	*name;
{
	sym_t	*sym,
		*prev = NULL,
		tmp;
	int	res;

	sym = symHead;
	bcopy(sym,&tmp,sizeof(tmp));
	while (sym)
	{
		if (name)
		{
			if (*name == *(sym->name))
			{
				myStrcmp(name,sym->name,res);
				if (res == 0)
				{
					return(1);
				}
			}
		}
#ifdef SYM_TORTURE
		else
		{
			/* 
			** This is for the torture test.
			** It is not used in production.  It just
			** checks each symbol table entry for 
			** bad pointers.
			*/
			int	dummy;

			strcmp("__DUMMY_SYMBOL_NAME__",sym->name);
			dummy = sym->type;
			dummy = sym->length;
			dummy = sym->array;
			dummy = sym->source;
		}
		prev = sym;
#endif
		sym = sym->next;
	}
	return(0);
}




sym_t *symGetSymbol(name)
	char	*name;
{
	sym_t	*sym;

	/*
	** Lookup the symbol tables
	*/
	if (*name == '$')
		sym = symHead;
	else
		sym = litHead;
	while (sym)
	{
		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
	{
		dst->val = (char *)malloc((src->length)+1);
		bcopy(src->val, dst->val, src->length);
		*(dst->val + src->length) = 0;
	}
	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);
	if (sym->name)
		free(sym->name);
	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->name = dst->val = NULL;
	dst->type = dst->array = dst->length = dst->source = 0;
	dst->next = NULL;
	if (src->name)
		dst->name = (char *)strdup(src->name);
	else
		dst->name = NULL;
	dst->type = src->type;
	dst->array = src->array;
	dst->length = src->length;
	if (dst->array == SCALAR)
	{
		dst->val = (char *)malloc((src->length)+1);
		bcopy(src->val,dst->val,src->length);
		*(dst->val + src->length) = 0;
	}
	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;
	int	length;

	/*
	** Unpack the current value into external form
	*/
	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);
	if (sym->length < 0)
	{
		runError("Invalid type for cast");
		exit(1);
	}
	sym->type = typeIndex;
#ifdef SYM_TORTURE
	symCheckSymbol(NULL);
#endif
	free(buf);
}


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

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

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

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


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

	new = symCreateSymbol(name,TYPE_CHAR,SCALAR);
        new->length = strlen(val);
	new->val = (char *)strdup(val);
}


sym_t *symCreateArrayGlobal(name,val, num)
	char	*name;
	char	*val[];
	int	num;
{
	sym_t	*new,
		*cur;
	type_t	*type;
	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)
	{
		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));
			bzero(cur->next->sym,sizeof(sym_t));
			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(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;
	char	nameBuf[10];
	int	index;
	type_t	*type;

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

	/*
	** 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(nameBuf,"%d",curLitIdx);
	sym->name = (char *)strdup(nameBuf);

	/*
	** 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));
	sym->array = SCALAR;
	sym->next = litHead;
	litHead = 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);
	}
	bzero(new,sizeof(sym_t));
	new->name = NULL;
	new->type = TYPE_INT;
	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);
	}
	bzero(new,sizeof(sym_t));
	new->name = NULL;
	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);
	}
	bzero(new,sizeof(sym_t));
	new->name = NULL;
	new->type = TYPE_CHAR;
	new->length = strlen(val);
	new->array = SCALAR;
	new->val = (char *)strdup(val);
	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);
	}
	bzero(new,sizeof(sym_t));
	new->name = NULL;
	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));
	bzero(val->sym,sizeof(sym_t));
	return(new);
}

sym_t *createArrayLiteral()
{
	sym_t	*array;
	char	nameBuf[10];

	array = createArray();
	curLitIdx++;
        sprintf(nameBuf,"%d",curLitIdx);
        array->name = (char *)strdup(nameBuf);
	array->next = litHead;
	litHead = array;
	return(array);
}



/*
** Macro handling routines
*/


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

	cur = macroHead;
	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;

	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;
	new->name = (char *)strdup(macro);
	new->next = macroHead;
	macroHead = new;
}


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

	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;
	new->name = (char *)strdup(macro);
	new->next = macroHead;
	macroHead = new;
}


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

	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;
	new->name = (char *)strdup(macro);
	new->next = macroHead;
	macroHead = new;
}



sym_t *symCreateMacroLiteral(macro)
	char	*macro;
{
	sym_t	*sym,
		*cur,
		*new;
	char	nameBuf[10];

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

	cur = litHead;
	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);
	}
	
	new = symdup(sym);
        curLitIdx++;
        sprintf(nameBuf,"%d",curLitIdx);
        new->name = (char *)strdup(nameBuf);
	new->next = litHead;
	litHead = new;
	return(new);
}
