#include <stdio.h>

#define STATIC	static

#include "skel.sst.h"
STATIC		/* This will apply to the array in the .sst file */
#include "skel.sst.c"

/* Table Walker State */
STATIC int	processing = 1;		/* are we running the table walker? */
STATIC int	sslPointer = 0;		/* index into S/SL table */
STATIC TableOperation	operation;	/* the current operation */

/* Tracing Control */
STATIC FILE	*tracefp = NULL;	/* if non-null, trace output here */

/* Abort flag */
STATIC int	aborted = 0;

/*
 * The Rule Call Stack implements Syntax/Semantic Language rule call and return.
 * Each time an oCall operation is executed, the table return address is pushed
 * onto the Rule Call Stack.  When an oReturn is executed, the return address
 * is popped from the stack.  An oReturn executed when the Rule Call Stack is
 * empty terminates table execution.
 */

STATIC int	sslStack[127];		/* The S/SL Rule Call Stack */
#define	sslStackSize	(sizeof sslStack/sizeof sslStack[0])
STATIC int	sslTop = 0;

/*
 * Set by the Choice Handler to indicate whether a match was made or the
 * otherwise path was taken.  Set to true if a match was made and false
 * otherwise.  This flag is used in input choices to indicate whether the
 * choice input token should be accepted or not.
 */

STATIC int	choiceTagMatched;	/* Choice Match Flag */

/*
 * These are used to hold the decoded parameter value to a parameterized
 * semantic operation and the result value returned by a choice semantic
 * operation or rule respectively.					 
 */

STATIC int	parameterValue;		/* Parameter for Semantic Operation */
STATIC int	resultValue;		/* Result from Semantic Operation */

/* S/SL System Failure Codes */

typedef enum {
	fSemanticChoiceFailed,			/* 0 */
	fChoiceRuleFailed			/* 1 */
} FailureCodes;				/* S/SL System Failure Code Type */


#define	maxErrors	20
STATIC int	noErrors = 0;		/* Error Counter */
STATIC ErrorCodes	firstFatalErrorCode = eSslStackOverflow;  /* fix */

/* Input Tokens */

/* Compound Input Tokens */
#define	firstCompoundToken	tInteger	/* fix */
#define	lastCompoundToken	tInteger	/* fix */

/* Input Interface */
STATIC FILE	*infp;
STATIC InputTokens	nextInputToken = tNewLine;
STATIC InputTokens	savedToken;

/*
 * The Compound Input Token Buffer:
 * When a compound input token is accepted from the input stream, its
 * associated value is saved in the compound token buffer for use by
 * the Semantic Mechanisms of the pass.
 */

STATIC InputTokens	compoundToken;	/* Last compound input token accepted */
STATIC int	compoundValue;	/* Its associated value; often a union (fix) */
STATIC char	*compoundText;	/* ... or textual ditto (fix) */

/* Line Counters */

STATIC int	nextLineNumber = 0;
STATIC int	lineNumber = 0;

/* Output Interface */
STATIC FILE	*outfp;

/* Variables Used in Syntax Error Recovery */
STATIC int	newInputLine = 0;
STATIC InputTokens	savedInputToken;


/* This procedure emits the error message associated with errCode */

STATIC void
Error(errCode)
	ErrorCodes errCode;
{
	char *msg;

	if (errCode == eNoError)
		abort();

	switch (errCode) {
	case eSyntaxError:
		msg = "Syntax error";
		break;
	case ePrematureEndOfFile:
		msg = "Unexpected end of file";
		break;
	case eExtraneousProgramText:
		msg = "Extraneous program text";
		break;
	case eSslStackOverflow:
		msg = "Nesting too deep";
		break;
	}
	if (errCode == eSyntaxError)
		/* Syntax errors are in the lookahead token */
		printf("Line %d: %s\n", nextLineNumber, msg);
	else
		/* Semantic errors are in the accepted token */
		printf("Line %d: %s\n", lineNumber, msg);

	++noErrors;

	if ((int)(errCode) >= (int)(firstFatalErrorCode)
	    || noErrors == maxErrors) {
		printf("*** Processing aborted\n");
		aborted = 1;
		processing = 0;
	}
}

/*
 * This procedure provides the interface to the previous pass.
 * It is reponsible for handling all input including line number
 * indicators and the values and text associated with input tokens.  
 */

STATIC void
AcceptInputToken()
{
	InputTokens	acceptedToken;
	int	inputInt;

	if (nextInputToken == tEndOfFile)
		abort();

	/* Accept Token */
	acceptedToken = nextInputToken;
	lineNumber = nextLineNumber;

	/* If the token is a compound token, read its associated value */
	if ((int)(acceptedToken) > (int)(firstCompoundToken)
	    && (int)(acceptedToken) < (int)(lastCompoundToken)) {
		compoundToken = acceptedToken;
		compoundValue = getw(infp);		/* fix */
	}

	/* Update Line Number */
	lineNumber = nextLineNumber;

	/* Read Next Input Token */
	newInputLine = 0;
	do {
		inputInt = getw(infp);
		nextInputToken = (InputTokens)inputInt;

		if (nextInputToken == tNewLine) {
			/* Update Line Counter and Set Flag */
			newInputLine = 1;
			++nextLineNumber;
		}
	} while (nextInputToken == tNewLine);

	/* Trace Input */
	if (tracefp)
	    printf("Input token accepted %d  Line %d  Next input token %d\n",
			acceptedToken, lineNumber, nextInputToken);
}

/* Emit an output token to the output stream */

STATIC void
EmitOutputToken(emittedToken) 
	OutputTokens	emittedToken;
{
	putw(emittedToken, outfp);

	/* Trace Output */
	if (tracefp)
		printf("Output token emitted %d\n", emittedToken);
}


/*
 * The constants, variables, types, modules and procedures used in
 * implementing the Semantic Mechanisms of the pass go here.  These
 * implement the facilities used in the semantic operations.
 */

/* Syntax Error Handling */

STATIC void
SslGenerateCompoundInputToken(expectedToken)
	InputTokens	expectedToken;
{
	if (nextInputToken != tSyntaxError && nextInputToken != tEndOfFile)
		abort();

	compoundToken = expectedToken;
	compoundValue = 0;

	switch (expectedToken) {
	case tInteger:
		compoundText = "0";
		break;
	case tString:
		compoundText = "'?'";
		break;
	case tIdent:
		compoundText = "IL";
		break;
	}
}

/*
 * This procedure handles syntax errors in the input to the Parser pass,
 * for Semantic passes this procedure will simply assert false since a
 * syntax error in input would indicate an error in the previous pass.
 *
 * Syntax error recovery:
 * When a mismatch occurs between the the next input token and the syntax
 * table, the following recovery is employed.
 *
 * If the expected token is tNewLine then if there has been no previous
 * syntax error on the line, ignore the error.  (A missing logical new line
 * is not a real error.)
 *
 * If the expected token is tNewLine or tSemicolon and a syntax error has
 * already been detected on the current logical line (flagged by
 * nextInputToken == tSyntaxError), then flush the input exit when a new
 * line or end of file is found.
 *
 * Otherwise, if this is the first syntax error detected on the line
 * (flagged by nextInputToken != tSyntaxError), then if the input token is
 * tEndOfFile then emit the ePrematureEndOfFile error code and terminate
 * execution.  Otherwise, emit the eSyntaxError error code and set the
 * nextInputToken to tSyntaxError to prevent further input exit when the
 * expected input is tSemicolon or tNewLine.
 *
 * If the expected token is not tSemicolon nor tNewLine and a syntax error
 * has already been detected on the current line (flagged by nextInputToken ==
 * tSyntaxError), then do nothing and continue as if the expected token had
 * been matched.
 */

STATIC void
SslSyntaxError()
{
	if (operation != oInput && operation != oInputAny)
		abort();

	if (nextInputToken == tSyntaxError) {
		/* Currently recovering from syntax error */
		if (sslTable[sslPointer] == (int)(tNewLine)
		    || sslTable[sslPointer] == (int)(tSemicolon)) {
			/* Complete recovery by synchronizing
			   input to a new line */
			nextInputToken = savedToken;
			newInputLine = 0;
			while (nextInputToken != tSemicolon
			       && nextInputToken != tEndOfFile
			       && !newInputLine)
				AcceptInputToken();
		}
	} else {
		/* First syntax error on the line */
		if (sslTable[sslPointer] == (int)(tNewLine)) {
			/* Ignore missing logical newlines */
		} else if (nextInputToken == tEndOfFile) {
			/* Flag error and terminate processing */
			Error(ePrematureEndOfFile);
			processing = 0;
		} else {
			Error(eSyntaxError);
			savedToken = nextInputToken;
			nextInputToken = tSyntaxError;
			lineNumber = nextLineNumber;
		}
	}

	/* If the expected input token is a compound token,
	   generate a dummy one. */
	if (sslTable[sslPointer] >= (int)(firstCompoundToken)
	    && sslTable[sslPointer] <= (int)(lastCompoundToken))
		SslGenerateCompoundInputToken(sslTable[sslPointer]);
}


STATIC void
SslTrace()
{
	printf("Table index %d  Operation %d  Argument %d\n",
		      sslPointer-1, operation, sslTable[sslPointer]);
}


STATIC void
SslFailure(failCode)
	FailureCodes failCode;
{
	printf("### S/SL program failure:  ");

	switch (failCode) {
	case fSemanticChoiceFailed:
		printf("Semantic choice failed\n");
		break;
	case fChoiceRuleFailed:
		printf("Choice rule returned without a value\n");
		break;
	}

	printf("while processing line %d\n", lineNumber);

	SslTrace();
	abort();
}


/*
 * This procedure performs both input and semantic choices.  It sequentially
 * tests each alternative value against the tag value, and when a match is
 * found, performs a branch to the corresponding alternative path.  If none
 * of the alternative values matches the tag value, sslTable interpretation
 * proceeds to the operation immediately following the list of alternatives
 * (normally the otherwise path).  The flag choiceTagMatched is set to true
 * if a match is found and false otherwise.
 */

STATIC void
SslChoice(choiceTag)
	int choiceTag;
{
	int	numberOfChoices, choicePointer;

	choicePointer = sslTable[sslPointer];
	choiceTagMatched = 0;

	for (numberOfChoices = sslTable[choicePointer++];
	     numberOfChoices > 0;
	     choicePointer += 2, --numberOfChoices) {
		if (sslTable[choicePointer] == choiceTag) {
			sslPointer = sslTable[choicePointer+1];
			choiceTagMatched = 1;
			return;
		}
	}
	sslPointer = choicePointer;
}


void
SslWalker()
{
	/* Initialize Input/Output */
	AcceptInputToken();

	/* Walk the S/SL Table */

	while (processing) {
		operation = (TableOperation)(sslTable[sslPointer++]);

		/* Trace Execution */
		if (tracefp)
			SslTrace();

		switch (operation) {
		case oCall:
			if (sslTop < sslStackSize) {
				sslStack[++sslTop] = sslPointer + 1;
				sslPointer = sslTable[sslPointer];
			} else {
				Error(eSslStackOverflow);
				processing = 0;
			}
			break;
		case oReturn:
			if (sslTop == 0)
				/* Return from main S/SL procedure */
				processing = 0;
			else
				sslPointer = sslStack[sslTop--];
			break;
		case oRuleEnd:
			SslFailure(fChoiceRuleFailed);
			break;
		case oJump:
			sslPointer = sslTable[sslPointer];
			break;
		case oInput:
			if (sslTable[sslPointer] == (int)(nextInputToken))
				AcceptInputToken();
			else
				/* Syntax error in input */
				SslSyntaxError();
			++sslPointer;
			break;
		case oInputAny:
			if (nextInputToken != tEndOfFile)
				AcceptInputToken();
			else
				/* Premature end of file */
				SslSyntaxError();
			break;
		case oInputChoice:
			SslChoice(nextInputToken);

			if (choiceTagMatched)
				AcceptInputToken();
			break;
		case oEmit:
			EmitOutputToken(sslTable[sslPointer]);
			++sslPointer;
			break;
		case oError:
			Error(sslTable[sslPointer]);
			++sslPointer;
			break;
		case oChoice:
			SslChoice(resultValue);
			break;
		case oChoiceEnd:
			SslFailure(fSemanticChoiceFailed);
			break;
		case oSetParameter:
			parameterValue = sslTable[sslPointer++];
			break;
		case oSetResult:
			resultValue = sslTable[sslPointer++];
			break;
		case oSetResultFromInput:
			resultValue = (int)nextInputToken;
			break;

		/* The Following Are Pass Dependent Semantic Mechanisms */

#ifdef	notdef
		case oUpdateOperation:
		    % Execute the Update Semantic Operation
		    % The parameter value for parameterized
		    % operations is in "parameterValue"		

		case oChoiceOperation:
		    % Execute the Choice Semantic Operation
		    % Leave the result value in "resultValue".
		    % The parameter value for parameterized 
		    % operations is in "parameterValue"		 
#endif
		    
		default:
			printf("Unknown operation %d\n", operation);
			abort();
		}
	}

	if (nextInputToken != tEndOfFile && !aborted)
		Error(eExtraneousProgramText);
}
