/* nl-symbol.c --- symbol handling routines for newLISP

    Copyright (C) 2007 Lutz Mueller

    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 3 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, see <http://www.gnu.org/licenses/>.

*/


#include "newlisp.h"
#include "protos.h"


extern CELL * cellMemory;
extern SYMBOL * trueSymbol;
extern SYMBOL * orSymbol;

SYMBOL * findInsertSymbol(char * key, int forceCreation);
int deleteSymbol(char * key);
void deleteContextSymbols(CELL * cell);
CELL dumpSymbol(char * name);
void collectSymbols(SYMBOL * sPtr, CELL * symbolList, CELL * * nextSymbol);
void symbolReferences(SYMBOL * sPtr,  CELL * symbolList, CELL * * nextSymbol);
static SYMBOL * root;	/* root symbol derived from context */

/* --------- return a list of all symbols in a context -------------- */


CELL * p_symbols(CELL * params)
{
SYMBOL * context;
CELL * symbolList;
CELL * nextSymbol;

symbolList = getCell(CELL_EXPRESSION);
nextSymbol = NULL;

if(params->type == CELL_NIL) 
	context = currentContext;
else
	getContext(params, &context);

if(context) /* check in case we are in debug mode */
	collectSymbols((SYMBOL *)((CELL *)context->contents)->aux, symbolList, &nextSymbol);
return(symbolList);
}


void collectSymbols(SYMBOL * sPtr, CELL * symbolList, CELL * * nextSymbol)
{
if(sPtr != NIL_SYM && sPtr != NULL)
	{
	collectSymbols(sPtr->left, symbolList, nextSymbol);
	if(*nextSymbol == NULL)
		{
		*nextSymbol = getCell(CELL_SYMBOL);
		(*nextSymbol)->contents = (UINT)sPtr;
		symbolList->contents = (UINT)*nextSymbol;
		}
	else 
		{
		(*nextSymbol)->next = getCell(CELL_SYMBOL);
		*nextSymbol = (*nextSymbol)->next;
		(*nextSymbol)->contents = (UINT)sPtr;
		}
	collectSymbols(sPtr->right, symbolList, nextSymbol);
	}
}



/* iterate thru symbol tree for a specific context
*/

CELL * p_dotree(CELL * params)
{
SYMBOL * context;
SYMBOL * symbol;
CELL * symbolList;
CELL * nextSymbol;
CELL * cell;
CELL * list;
int resultIdxSave;

if(params->type != CELL_EXPRESSION)
	return(errorProcExt(ERR_LIST_EXPECTED, params));

list = (CELL *)params->contents;
if(list->type == CELL_SYMBOL)
	symbol = (SYMBOL *)list->contents;
else if(list->type == CELL_DYN_SYMBOL)
	symbol = getDynamicSymbol(list);
else
	return(errorProcExt(ERR_SYMBOL_EXPECTED, list));

if(isProtected(symbol->flags))
	return(errorProcExt2(ERR_SYMBOL_PROTECTED, stuffSymbol(symbol)));

pushEnvironment((CELL *)symbol->contents);
pushEnvironment((UINT)symbol);

symbol->contents = (UINT)copyCell(nilCell);

getContext(list->next, &context);
if(!context) return(nilCell); /* for debug mode */
cell = nilCell;

symbolList = getCell(CELL_EXPRESSION);
nextSymbol = NULL;
collectSymbols((SYMBOL *)((CELL *)context->contents)->aux, symbolList, &nextSymbol);

resultIdxSave = resultStackIdx;
list = (CELL *)symbolList->contents;
while(list != nilCell)
    {
    cleanupResults(resultIdxSave);
    deleteList((CELL *)symbol->contents);
    symbol->contents = (UINT)copyCell(list);
    cell = evaluateBlock(params->next);
    list = list->next;
    }

deleteList((CELL *)symbol->contents);

symbol = (SYMBOL*)popEnvironment();
symbol->contents = (UINT)popEnvironment();

deleteList(symbolList);

return(copyCell(cell));
}



SYMBOL * lookupSymbol(char * token, SYMBOL * context)
{
root = (SYMBOL *)((CELL *)context->contents)->aux;

return(findInsertSymbol(token, LOOKUP_ONLY));
}



/* 
   if forceFlag is TRUE then 
       create the symbol, if not found in the context 
       specified in that context
   else
       if not found try to inherit from MAIN as a global
       or primitive, else create it in context specified
*/


SYMBOL * translateCreateSymbol
	(char * token, int type, SYMBOL * context, int forceFlag)
{
SYMBOL * sPtr;
CELL * cell = NULL;
size_t len;

/* for the first symbol (also a context) context is NULL */
if(context == NULL)
	root = NULL;
else
	{
	cell = (CELL *)context->contents;
	root = (SYMBOL *)cell->aux;
	}

if(forceFlag)
	sPtr = findInsertSymbol(token, FORCE_CREATION);
else /* try to inherit from MAIN, if not here create in current context */
	{
	sPtr = findInsertSymbol(token, LOOKUP_ONLY);
	if(sPtr == NULL)
		{
		if(context != mainContext)
			{
			root = (SYMBOL *)((CELL *)mainContext->contents)->aux;
			sPtr = findInsertSymbol(token, LOOKUP_ONLY);
			/* since 7.2.7 only inherit primitives and other globals */
			if(sPtr != NULL && !(sPtr->flags & SYMBOL_GLOBAL))
				{
				if(symbolType(sPtr) != CELL_CONTEXT
				    || (SYMBOL *)((CELL*)sPtr->contents)->contents != sPtr)
					sPtr = NULL;
				}
			root = (SYMBOL *)cell->aux;
			}
		if(sPtr == NULL)
			sPtr = findInsertSymbol(token, FORCE_CREATION);
		}
	}

/* root might have changed, if new symbol was inserted */
if(context != NULL)
	cell->aux = (UINT)root;

/* the symbol existed already, return */
if(sPtr->contents != 0) return(sPtr);
	
/* a new symbol has been allocated by findInsertSymbol() */
if(type != CELL_PRIMITIVE)
	{
	len = strlen(token);
	sPtr->name = (char *)allocMemory(len + 1);
	memcpy(sPtr->name, token, len + 1);
 	cell = copyCell(nilCell); 
	sPtr->contents = (UINT)cell;
	/* don't if this is a context variable (not in MAIN) 8.9.8 
	   but could this  protect/make global context vars in MAIN?
	   new and def-new inhibit targets to be MAIN, cant come from there 
    */
	if(type == CELL_CONTEXT && context == mainContext)
		{
		cell->type = CELL_CONTEXT;
		cell->contents = (UINT)sPtr;
		cell->aux = 0;
		sPtr->flags |= (SYMBOL_PROTECTED | SYMBOL_GLOBAL);
		}
	}
else
	sPtr->name = token;


sPtr->context = context;
return(sPtr);
}

/* ------------------------- dump RB tree info of a symbol -------------------- */

#ifdef SYMBOL_DEBUG
CELL * p_dumpSymbol(CELL * params)
{
char * name;
SYMBOL * sPtr;

getString(params, &name);

sPtr = findInsertSymbol(name, LOOKUP_ONLY);

if(sPtr == NULL)
	return(nilCell);

varPrintf(OUT_DEVICE, "name=%s color=%s parent=%s left=%s right=%s\n", 
	sPtr->name,
	(sPtr->color == RED) ? "red" : "black",
	(sPtr->parent) ? sPtr->parent->name : "ROOT",
	sPtr->left->name,
	sPtr->right->name);

return(trueCell);
}
#endif



/* ----------------------------- delete a symbol --------------------------- */
int references(SYMBOL * sPtr, int replaceFlag);

CELL * p_deleteSymbol(CELL * params)
{
SYMBOL * sPtr;
CELL * cell;

cell = evaluateExpression(params);
if(cell->type == CELL_SYMBOL || cell->type == CELL_CONTEXT)
	sPtr = (SYMBOL*)cell->contents;
else if(cell->type == CELL_DYN_SYMBOL)
	sPtr = getDynamicSymbol(cell);
else return(errorProcExt(ERR_SYMBOL_OR_CONTEXT_EXPECTED, params));

if(sPtr == mainContext) return(nilCell);

if(symbolType(sPtr) == CELL_CONTEXT)
	{
 	if(cell->type == CELL_SYMBOL) 
		cell = (CELL*)sPtr->contents;
	sPtr->flags &= ~SYMBOL_PROTECTED;
	}

if(sPtr->flags & (SYMBOL_PROTECTED | SYMBOL_BUILTIN) )
	return(nilCell);

if(getFlag(params->next))
	{
	if(references(sPtr, FALSE) > 1)
		return(nilCell);
	}

if(cell->type == CELL_CONTEXT)
	{
	deleteContextSymbols(cell);	
	cell->type = CELL_SYMBOL;
	deleteList((CELL *)sPtr->contents);
	sPtr->contents = (UINT)nilCell;
	}
else 
	deleteFreeSymbol(sPtr);

return(trueCell);
}


void deleteContextSymbols(CELL * cell)
{
SYMBOL * context;
CELL * symbolList;
CELL * nextSymbol;

context = (SYMBOL *)cell->contents;

symbolList = getCell(CELL_EXPRESSION);
nextSymbol = NULL;
collectSymbols((SYMBOL *)((CELL *)context->contents)->aux, symbolList, &nextSymbol);

nextSymbol = (CELL *)symbolList->contents;
while(nextSymbol != nilCell)
	{
	deleteFreeSymbol((SYMBOL*)nextSymbol->contents);
	nextSymbol = nextSymbol->next;
	}
	
deleteList(symbolList);
}



void deleteFreeSymbol(SYMBOL * sPtr)
{
SYMBOL * context;

context = sPtr->context;
root = (SYMBOL *)((CELL *)context->contents)->aux;

if(!deleteSymbol(sPtr->name))
	return;

((CELL *)context->contents)->aux = (UINT)root; /* root may have changed */

deleteList((CELL *)sPtr->contents);

references(sPtr, TRUE);
freeMemory(sPtr->name);
freeMemory(sPtr);
}



void makeContextFromSymbol(SYMBOL * symbol, SYMBOL * treePtr)
{
CELL * contextCell;

contextCell = getCell(CELL_CONTEXT);
contextCell->contents = (UINT)symbol;
contextCell->aux = (UINT)treePtr;
symbol->contents = (UINT)contextCell;
symbol->context = mainContext;
symbol->flags |= (SYMBOL_PROTECTED | SYMBOL_GLOBAL);
}


int references(SYMBOL * sPtr, int replaceFlag)
{
CELL * blockPtr;
int i, count;

blockPtr = cellMemory;
count = 0;
while(blockPtr != NULL)
	{
	for(i = 0; i < MAX_BLOCK; i++)
		{
		if( ( *(UINT *)blockPtr == CELL_SYMBOL && blockPtr->contents == (UINT)sPtr) ||
		    ( *(UINT *)blockPtr == CELL_CONTEXT && blockPtr->contents == (UINT)sPtr) )
			{
			count++;
			if(replaceFlag) blockPtr->contents = (UINT)nilSymbol;
			}
		blockPtr++;
		}
	blockPtr = blockPtr->next;
	}

return(count);
}

CELL * p_name(CELL * params)
{
SYMBOL * sPtr;
CELL * cell;

cell = evaluateExpression(params);
if(cell->type == CELL_SYMBOL || cell->type == CELL_CONTEXT)
	sPtr = (SYMBOL *)cell->contents;
else
	return(errorProcExt(ERR_SYMBOL_OR_CONTEXT_EXPECTED, cell));

if(getFlag(params->next))
	return(stuffString(((SYMBOL*)sPtr->context)->name));
return(stuffString(sPtr->name));
}

/* -------------------------------------------------------------------------

   Red-Black Balanced Binary Tree Algorithm adapted from:

   Thomas Niemann thomasn@epaperpress.com

   See also:

   http://epaperpress.com/sortsearch/index.html

   and:

   Thomas H. Cormen, et al
   Introduction to Algorithms
   (MIT Electrical Engineering and Computer Science)
   (C) 1990 MIT Press

*/


#define compLT(a,b) (a < b)
#define compEQ(a,b) (a == b)

#define BLACK 0
#define RED 1

#define NIL_SYM &sentinel	/* all leafs are sentinels */

SYMBOL sentinel = {
	0, 		/* pretty print */
	BLACK,		/* color */
	"NIL",		/* name */
	0,		/* contents */
	NULL,		/* context */
	NULL,		/* parent */
	NIL_SYM,	/* left */
 	NIL_SYM 	/* right */
	};

void rotateLeft(SYMBOL* x);
void rotateRight(SYMBOL * x);
static void insertFixup(SYMBOL * x);
void deleteFixup(SYMBOL *x);

/* --------------------------------------------------------------------

   lookup the symbol with name key, if it does not exist and the
   forceCreation flag is set, create and insert the symbol and
   return a pointer to the new symbol. If the context passed is empty
   then it's treePtr (root) will be the new symbol.

*/


SYMBOL * findInsertSymbol(char * key, int forceCreation) 
{
SYMBOL *current, *parent, *x;

/* find future parent */
current = (root == NULL) ? NIL_SYM : root;
parent = 0;

while (current != NIL_SYM)
	{
	if(strcmp(key, current->name) == 0) /* already exists */
            return(current);

	parent = current;
	current = (strcmp(key, current->name) < 0) ? 
		current->left : current->right;
	}

/* if forceCreation not specified just return */
if(forceCreation == LOOKUP_ONLY) return(NULL);

/* allocate new symbol */
x = (SYMBOL *)callocMemory(sizeof(SYMBOL));

x->parent = parent;
x->left = NIL_SYM;
x->right = NIL_SYM;
x->color = RED;

/* insert node in tree */
if(parent) 
	{
      if(strcmp(key, parent->name) < 0)
            parent->left = x;
      else
            parent->right = x;
	} 
else 
	root = x;

insertFixup(x);


/* return new node */

++symbolCount;
return(x);
}


/* --------------------------------------------------------------------
   extract symbol in context from tree, return 1 if deleted or 0 if it 
   couldn't be found.

*/

int deleteSymbol(char * key)
{
SYMBOL *x, *y, *z;
int color;

/* find node in tree */
z = (root == NULL) ? NIL_SYM : root;

while(z != NIL_SYM)
	{
	if(strcmp(key, z->name) == 0) 
		break;
	else
		z = (strcmp(key, z->name) < 0) ? z->left : z->right;
	}

if (z == NIL_SYM) return(0); /* key to delete not found */


if (z->left == NIL_SYM || z->right == NIL_SYM)
	{
	/* y has a NIL_SYM node as a child */
	y = z;
	}
else 
	{
	/* find tree successor with a NIL_SYM node as a child */
	y = z->right;
	while (y->left != NIL_SYM) y = y->left;
	}

/* x is y's only child */
if (y->left != NIL_SYM)
	x = y->left;
else
	x = y->right;

/* remove y from the parent chain */
x->parent = y->parent;
if (y->parent)
	{
	if (y == y->parent->left)
		y->parent->left = x;
        else
            y->parent->right = x;
	}
else
	root = x;


color = y->color;
if (y != z)
	{
	/* swap y and z */
	y->left = z->left;
	y->right = z->right;
	y->parent = z->parent;

	if(z->parent)
		{
		if(z->parent->left == z)
			z->parent->left = y;
		else
			z->parent->right = y;
		}
	else root = y;

	y->right->parent = y;
	y->left->parent = y;

	y->color = z->color;
	}

if (color == BLACK)
	deleteFixup (x);

--symbolCount;
return TRUE;
}



/* -------------------------------------------------------------------- */

void rotateLeft(SYMBOL* x) 
{
SYMBOL* y;

y = x->right;

/* establish x->right link */
x->right = y->left;
if (y->left != NIL_SYM) 
	y->left->parent = x;

/* establish y->parent link */
if(y != NIL_SYM) 
	y->parent = x->parent;

if (x->parent)
	{
	if (x == x->parent->left)
      	x->parent->left = y;
      else
      	x->parent->right = y;
	} 
else 
	root = y;


/* link x and y */
y->left = x;
if (x != NIL_SYM) 
	x->parent = y;
}


void rotateRight(SYMBOL * x)
{
SYMBOL * y;

y = x->left;

/* establish x->left link */
x->left = y->right;
if (y->right != NIL_SYM)
	y->right->parent = x;

/* establish y->parent link */
if (y != NIL_SYM) 
	y->parent = x->parent;

if (x->parent) 
	{
      if (x == x->parent->right)
            x->parent->right = y;
      else
            x->parent->left = y;
	}
else
	root = y;

/* link x and y */
y->right = x;
if (x != NIL_SYM) 
	x->parent = y;
}


static void insertFixup(SYMBOL * x)
{
SYMBOL * y;

/* check Red-Black properties */
while (x != root && x->parent->color == RED)
	{
	/* we have a violation */
	if (x->parent == x->parent->parent->left)
		{
        y = x->parent->parent->right;
        if (y->color == RED) 
			{
			/* uncle is RED */
			x->parent->color = BLACK;
			y->color = BLACK;
			x->parent->parent->color = RED;
			x = x->parent->parent;
			} 
		else 
			{
           	/* uncle is BLACK */
           	if (x == x->parent->right)
				{
           		/* make x a left child */
           		x = x->parent;
           		rotateLeft(x);
				}

			/* recolor and rotate */
			x->parent->color = BLACK;
			x->parent->parent->color = RED;
			rotateRight(x->parent->parent);
           }
		} 
	else 
		{

		/* mirror image of above code */
		y = x->parent->parent->left;
		if (y->color == RED) 
			{
			/* uncle is RED */
			x->parent->color = BLACK;
			y->color = BLACK;
			x->parent->parent->color = RED;
			x = x->parent->parent;
			} 
		else 
			{
			/* uncle is BLACK */
			if (x == x->parent->left) 
				{
				x = x->parent;
				rotateRight(x);
				}
			x->parent->color = BLACK;
			x->parent->parent->color = RED;
			rotateLeft(x->parent->parent);
			}
		}
	}

root->color = BLACK;
}


void deleteFixup(SYMBOL *x)
{
SYMBOL * w;

while (x != root && x->color == BLACK)
	{
	if (x == x->parent->left)
		{
            w = x->parent->right;
            if (w->color == RED)
			{
			w->color = BLACK;
			x->parent->color = RED;
			rotateLeft (x->parent);
			w = x->parent->right;
			}
            if (w->left->color == BLACK && w->right->color == BLACK)
			{
			w->color = RED;
			x = x->parent;
			} 
		else 
			{
			if (w->right->color == BLACK)
				{
				w->left->color = BLACK;
				w->color = RED;
				rotateRight (w);
				w = x->parent->right;
				}
			w->color = x->parent->color;
			x->parent->color = BLACK;
			w->right->color = BLACK;
			rotateLeft (x->parent);
			x = root;
			}
		} 
	else 
		{
            w = x->parent->left;
            if (w->color == RED)
			{
			w->color = BLACK;
			x->parent->color = RED;
			rotateRight (x->parent);
			w = x->parent->left;
			}
            if (w->right->color == BLACK && w->left->color == BLACK)
			{
			w->color = RED;
			x = x->parent;
			} 
		else 
			{
			if (w->left->color == BLACK)
				{
				w->right->color = BLACK;
				w->color = RED;
				rotateLeft (w);
				w = x->parent->left;
				}
			w->color = x->parent->color;
			x->parent->color = BLACK;
			w->left->color = BLACK;
			rotateRight (x->parent);
			x = root;
			}
		}
	}

x->color = BLACK;
}


/* eof */




syntax highlighted by Code2HTML, v. 0.9.1