/* nl-debug.c --- debugging functions 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"

#ifdef WIN_32
#define fgets win32_fgets
#endif

extern FILE * IOchannel;
extern int evalSilent;

int matchString(char * buffer,  char * string, int * length);
int stringComp(char * buffer, char * string);
int printFunction(CELL * cell);
void getDebuggerInput(char * msg);
void allocateDebugStack(void);

extern SYMBOL * currentFunc;
extern SYMBOL * timerEvent;
extern SYMBOL * symHandler[];
extern int currentSignal;

int currentLevel = 0;
int debugStackIdx = 0;
UINT * debugStack;

char preStr[8] = "#";
char postStr[8] = "#";
char headerStr[16] = "\n-----\n\n";
char footerStr[32] = " s|tep n|ext c|ont q|uit > ";

#define pushDebugStack(A) (*(debugStack + debugStackIdx++) = (UINT)(A))
#define popDebugStack() (*(debugStack + --debugStackIdx))

#define DEBUG_ENTRY "->"
#define DEBUG_EXIT "<-"


void openTrace(void)	
	{
	if(traceFlag) return;
	traceFlag = TRUE;
	currentFunc = nilSymbol;
	debugStackIdx = 0;
	allocateDebugStack();
	}

void closeTrace(void)
	{
/*	printf("closing trace\n"); */
	traceFlag = 0;
	if(debugStack) free(debugStack);
	debugStack = NULL;
	}



CELL * p_debug(CELL * params)
{
CELL * result;

openTrace();
result = copyCell(evaluateExpression(params));
closeTrace();

return(result);
}


CELL * p_trace(CELL * params)
{
if(params != nilCell)
	{
	if(getFlag(params))
		openTrace();
	else
		closeTrace();
	}

return((traceFlag == 0 ? nilCell : trueCell));
}

CELL * p_traceHighlight(CELL * params)
{
char * pre, * post, * header, * footer;

params = getString(params, &pre);
params = getString(params, &post);

strncpy(preStr, pre, 7);
strncpy(postStr, post, 7);

if(params != nilCell)
	{
	params = getString(params, &header);
	strncpy(headerStr, header, 15);
	}

if(params != nilCell)
	{
	getString(params, &footer);
	strncpy(footerStr, footer, 31);
	}


return(trueCell);
}


void traceEntry(CELL * cell, CELL * pCell, CELL * args)
{
int defaultFuncFlag = FALSE;

if(traceFlag & (TRACE_IN_ENTRY | TRACE_IN_EXIT | TRACE_DEBUG_NEXT)) return;
traceFlag |= TRACE_IN_ENTRY;

if(traceFlag & TRACE_SIGNAL)
	{
	traceFlag &= ~TRACE_SIGNAL;
	executeSymbol(symHandler[currentSignal - 1], stuffInteger(currentSignal));
	traceFlag &= ~TRACE_IN_ENTRY;
	return;
	}

if(traceFlag & TRACE_SIGINT)
	{
	traceFlag &= ~TRACE_SIGINT;
	longjmp(errorJump, ERR_USER_RESET);
	}
	
if(traceFlag & TRACE_TIMER)
	{
	traceFlag &= ~TRACE_TIMER;
	executeSymbol(timerEvent, NULL);
	traceFlag &= ~TRACE_IN_ENTRY;
	return;
	}
	
if(debugStackIdx > 1)
	{
	if(printFunction(cell))
		getDebuggerInput(DEBUG_ENTRY);
	if(!traceFlag) return;
	}

if(traceFlag & TRACE_DEBUG_NEXT)
	{
	traceFlag &= ~TRACE_IN_ENTRY;
	return;
	}

if(pCell->type == CELL_CONTEXT)
	{
	defaultFuncFlag = TRUE;
	currentFunc = translateCreateSymbol(
			((SYMBOL*)pCell->contents)->name,
			CELL_NIL,
			(SYMBOL*)pCell->contents,
			TRUE);

	pCell = (CELL *)currentFunc->contents;
	}

if((pCell->type == CELL_LAMBDA || pCell->type == CELL_MACRO)
	&& args->type == CELL_SYMBOL)
	{
	if(debugStackIdx == 0) /* startup */
		traceFlag &= ~TRACE_DEBUG_NEXT;
	
	if(!defaultFuncFlag)
		currentFunc = (SYMBOL *)args->contents;	
	pushDebugStack(recursionCount);
	pushDebugStack(currentFunc);
	}

traceFlag &= ~TRACE_IN_ENTRY;
}


void traceExit(CELL * result, CELL * cell, CELL * pCell, CELL * args)
{
if(traceFlag & (TRACE_IN_ENTRY | TRACE_IN_EXIT | TRACE_SIGNAL | TRACE_SIGINT | TRACE_TIMER)) return;
traceFlag |= TRACE_IN_EXIT;

if(traceFlag & TRACE_DEBUG_NEXT)
	{
	if(currentLevel >= recursionCount)
		traceFlag &= ~TRACE_DEBUG_NEXT;
	else 
		{
		traceFlag &= ~TRACE_IN_EXIT;
		return;
		}
	}

if( (pCell->type == CELL_LAMBDA || pCell->type == CELL_MACRO)
		&& args->type == CELL_SYMBOL)
	{
	if((UINT)recursionCount == *(debugStack + debugStackIdx - 2) )
		{
		debugStackIdx -= 2;
		if(debugStackIdx > 0)
			currentFunc = (SYMBOL *)*(debugStack + debugStackIdx - 1);
		if(debugStackIdx == 0)
			traceFlag &= ~TRACE_DEBUG_NEXT;
		}
	}

if(printFunction(cell))
	{
	varPrintf(OUT_CONSOLE, "\nRESULT: ");
	printCell(result, TRUE, OUT_CONSOLE);
	varPrintf(OUT_CONSOLE, "\n");

	if(debugStackIdx > 0)
		{
		getDebuggerInput(DEBUG_EXIT);
		if(!traceFlag) return;
		}
	}

if(traceFlag & TRACE_DEBUG_NEXT)
	currentLevel = recursionCount;

traceFlag &= ~TRACE_IN_EXIT;
}


void getDebuggerInput(char * msg)
{
char command[MAX_LINE];
char * context;
jmp_buf errorJumpSave;
int resultStackIdxSave;
SYMBOL * contextSave;

while(TRUE)
	{

	if(currentContext != mainContext)
		context = currentContext->name;
	else context = "";


	if(!evalSilent)
		varPrintf(OUT_CONSOLE, "\n[%s %d %s]%s", msg, recursionCount, context, footerStr);
	else evalSilent = FALSE;

	fgets(command, MAX_LINE - 1, IOchannel);

	if( (strcmp(command, "n\n") == 0) || (strcmp(command, "n\r\n") == 0))
		{
		traceFlag |= TRACE_DEBUG_NEXT;
		currentLevel = recursionCount;
		break;
		}
	else if( (strcmp(command, "s\n") == 0) || (strcmp(command, "s\r\n") == 0))
		{
		traceFlag &= ~TRACE_DEBUG_NEXT;
		break;
		}
	else if( (strcmp(command, "q\n") == 0) || (strcmp(command, "q\r\n") == 0))
		{
		closeTrace();
		longjmp(errorJump, ERR_USER_RESET);
		}
	else if( (strcmp(command, "c\n") == 0) || (strcmp(command, "c\r\n") == 0))
		{
		closeTrace();
		break;
		}

	resultStackIdxSave = resultStackIdx;
	memcpy(errorJumpSave, errorJump, sizeof(jmp_buf));
	contextSave = currentContext;
	currentContext = currentFunc->context;
	if(setjmp(errorJump))
		{
		cleanupResults(resultStackIdxSave);
		goto DEBUG_EVAL_END;
		}

	executeCommandLine(command, OUT_CONSOLE, NULL);	

	DEBUG_EVAL_END:
	currentContext = contextSave;
	memcpy(errorJump, errorJumpSave, sizeof(jmp_buf));
	}
}


int printFunction(CELL * cell)
{
char * funcStr;
char * expStr;
int start, length;
STREAM strStream;

if(currentFunc == nilSymbol) return FALSE;

openStrStream(&strStream, MAX_STRING, 0);
printSymbol(currentFunc, (UINT)&strStream);
length = strlen(strStream.buffer);
funcStr = allocMemory(length + 1);
memcpy(funcStr, strStream.buffer, length + 1);
*(funcStr + length) = 0;

openStrStream(&strStream, MAX_STRING, 0);
printCell((CELL *)cell, TRUE, (UINT)&strStream);
expStr = strStream.buffer;
start = matchString(funcStr, expStr, &length);
closeStrStream(&strStream);

if(start == -1 || length == 0)
	{
	free(funcStr);
	return FALSE;
	}

varPrintf(OUT_CONSOLE, headerStr);

expStr = allocMemory(length + 1);
strncpy(expStr, funcStr + start, length);
*(expStr + length) = 0;
*(funcStr + start) = 0;

varPrintf(OUT_CONSOLE, "%s%s%s%s%s", 
	funcStr, preStr, expStr, postStr, funcStr + start + length);

free(funcStr);

return TRUE;
}


void allocateDebugStack(void)
{
debugStack = (UINT *)allocMemory(MAX_CPU_STACK * 2 * sizeof(UINT));
}

/* search for a string skipping white space
*/
int matchString(char * buffer,  char * string, int * length)
{
int position, flag;

/* strip leading white space */
while(*string <= ' ' && *string > 0) string++;

position = flag = 0;

while(*buffer)
	{
	if(*buffer > ' ')
		if((*length = stringComp(buffer, string)) > 0)
			{
			flag = TRUE;
			break;
			}
	position++;
	buffer++;
	}

if(!flag) return(-1);
return(position);
}


/* compare strings skipping white space
*/
int stringComp(char * buffer, char * string)
{
char * start;

start = buffer;
while(*string && *buffer)
	{
	if(*string > ' ')
		{
		if(*buffer > ' ')
			{
			if(*string != *buffer) return(0);
			string++;
			buffer++;
			}
		else buffer++;
		}
	else string++;
	}
	
if(*string == 0)
	return(buffer - start);
	
return(0);
}


/* eof */




syntax highlighted by Code2HTML, v. 0.9.1