/*
 *	C S/SL Processor
 *
 *	Translated from Turing S/SL Processor V3.01
 *	by Rayan Zachariassen, February 1988.
 *
 *	Original code by James R. Cordy,
 *	14 January 1980  (Revised 2 March 1983)
 *
 *
 * Copyright 1979,1980,1981,1983,1988  The University of Toronto 
 *
 * This implementation is freely redistributable with the following
 * two conditions:
 *
 * 1.   This notice, stating the copyright, origin, and conditions on use,
 *      of this software, must be retained at the beginning of this file
 *      and of any derived works.
 *
 * 2.   Any use of S/SL technology should acknowledge that fact, as well as
 *      the University of Toronto as the origin of the S/SL system, and
 *      J.R. Cordy and R.C. Holt as its authors.
 *
 *
 * For information about S/SL, see the paper:
 *
 *      "An Introduction to S/SL: Syntax/Semantic Language"
 *      by R.C. Holt, J.R. Cordy, and D.B. Wortman,
 *      in ACM Transactions on Programming Languages and Systems (TOPLAS),
 *      Vol 4, No. 2, April 1982, Pages 149-178.
 *
 * The authors may be contacted through the Computer Systems Research Institute
 * at the University of Toronto.
 */

/*
 * Note: The structure and feel of this program is that of a Pascal
 * program because it was transliterated from the Turing version of
 * the Pascal S/SL Processor.
 *
 *
 * This program is the processor for Syntax/Semantic Langauge programs.
 * An S/SL program must be processed by this program, which will output
 * C declarations for the constants defining the input tokens, output 
 * tokens, error codes, type values and semantic operation codes used in
 * the S/SL program and an array constant declaration for the S/SL table
 * for the program.  These declarations must be merged into the global
 * declarations C S/SL Walker.		
 *
 * Input files:
 *   sourcefp - the S/SL program source	
 *
 * Output files:
 *   headerfp - the output constant definitions
 *		 for the program
 *   tablefp - the output S/SL table file for 
 *		 the program
 *   listFile -   a listing of the S/SL source 
 *		 program with table coordinates 
 *		 in the left margin (if requested) 
 *
 * The following are recognized options:
 *
 *  l - produce a listing of the S/SL source
 *	program with table coordinates in the 
 *	left margin in listfp
 *  s - summarize usage of symbol and output 
 *	tables
 *  T - trace S/SL processor execution		
 *
 */

#include <stdio.h>
#include <sys/types.h>
#include <ctype.h>
#include "hostenv.h"

#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif

extern int optind;
extern char *optarg;

/* extern char *malloc(); */


#ifndef __STDC__
extern FILE *fopen();
/* extern char *sprintf();
   extern int fprintf();
   extern int printf(); */
#ifndef strcpy
extern char *strcpy();
#endif
#ifndef strcmp
extern int strcmp();
#endif
#ifndef strlen
extern int strlen();
#endif
extern int atoi();
extern int getopt();
/* extern int tolower(); */
/* extern void abort(); */
/* extern void exit(); */
#endif

#ifndef	SELFCONTAINED
#include "ssl_sst.h"
#endif
/* The S/SL table file produced by the S/SL Processor */
#include "ssl_sst.c"

#define	true	1
#define	false	0

#define	assertionFailure	abort()

#define	xSymbolClass(X)		X = symClass[symIndex]
#define	SymbolValue(X)		X = symValue[symIndex]
#define	SymbolName(X)		X = symText[symIndex]
#define	CurrentSymbolIndex(X)	X = symIndex
#define	ClassOfChoice(X)	X = choiceClass[choiceTop]
#define	CurrentClass(X)		X = symCurrentClass
#define	SymbolParameterClass(X)	X = symParmClass[symIndex]
#define	SymbolResultClass(X)	X = symResultClass[symIndex]
#define	CycleDepth(X)		X = cycleTop
#define	CycleExits(X)		X = exitTop - cycleExitIndex[cycleTop]
#define	EndRules()		/*(void)0*/
#define	StartRules()		/*(void)0*/
#define	BeginRule()		/*(void)0*/
#define	SaveRule()		/*(void)0*/

char const * machineOpNames[200] = {
  "oCall", "oReturn", "oRuleEnd", "oJump",
  "oInput", "oInputAny", "oInputChoice",
  "oEmit", "oError",  "oChoice", "oChoiceEnd",
  "oSetParameter", "oSetResult",
  "oSetResultFromInput",
  NULL };

const char * errorCodeNames[100] = { "eNoError", "eSyntaxError",
				     "ePrematureEndOfFile",
				     "eExtraneousProgramText", NULL };

#ifdef	SELFCONTAINED
/*
 * Primitive S/SL Table Operations:
 * These will remain the same independent of the
 * pass and form the fundamental table operations. 
 */

typedef enum {
	oCall = 0,					/* 0 */
	oReturn,					/* 1 */
	oRuleEnd,					/* 2 */
	oJump,						/* 3 */
	oInput,						/* 4 */
	oInputAny,					/* 5 */
	oInputChoice,					/* 6 */
	oEmit,						/* 7 */
	oError,						/* 8 */
	oChoice,					/* 9 */
	oChoiceEnd,					/* 10 */
	oSetParameter,					/* 11 */
	oSetResult,					/* 12 */
	oSetResultFromInput,				/* 13 */

/*
 * Pass Dependent Semantic Operations:
 * These will be different for each pass.  The
 * semantic operations are implemented by the
 * Semantic Mechanisms of the pass.
 * There are two basic kinds of semantic operations:
 * Update operations, which cause an update to the
 * Semantic Mechanism, and Choice operations, which
 * return a value based on the state of the Semantic
 * Mechanism which is then used as the tag in a semantic
 * choice.  Both Update and Choice operations may be
 * parameterized by a single constant value.		
 */

	oEnterCall,					/* 14 */
	oEmitNullCallAddress,				/* 15 */
	oResolveCalls,					/* 16 */
	oSetClass,					/* 17 */
	oSetClassFromSymbolClass,			/* 18 */
	oxSetClassFromSymbolValue,			/* 19 */
	oySetClassFromSymbolResultClass,		/* 20 */
	ozSetClassFromSymbolParameterClass,		/* 21 */
	ovSetClassFromChoiceClass,			/* 22 */
	oChooseClass,					/* 23 */
	oSetClassValue,					/* 24 */
	owSetClassValueFromSymbolValue,			/* 25 */
	oIncrementClassValue,				/* 26 */
	oEnterNewSymbol,				/* 27 */
	oLookupSymbol,					/* 28 */
	oChangeSymbolClass,				/* 29 */
	oChooseSymbolClass,				/* 30 */
	oVerifySymbolClass,				/* 31 */
	oxEnterNewSymbolValue,				/* 32 */
	oEnterSecondNewSymbolValue,			/* 33 */
	oEnterSymbolValueFromAddress,			/* 34 */
	oxChooseSymbolValue,				/* 35 */
	oEmitSymbolValue,				/* 36 */
	oxVerifySymbolClassValue,			/* 37 */
	oxEnterSymbolParameterClass,			/* 38 */
	oyEnterSymbolResultClass,			/* 39 */
	oyChooseSymbolResultClass,			/* 40 */
	oSaveEnclosingSymbol,				/* 41 */
	oRestoreEnclosingSymbol,			/* 42 */
	oSaveCurrentSymbol,				/* 43 */
	oRestoreCurrentSymbol,				/* 44 */
	oPushCycle,					/* 45 */
	oPopCycle,					/* 46 */
	oChooseCycleDepth,				/* 47 */
	oEmitCycleAddress,				/* 48 */
	oEnterCycleExit,				/* 49 */
	oResolveCycleExits,				/* 50 */
	oxChooseCycleExits,				/* 51 */
	oPushChoice,					/* 52 */
	oPopChoice,					/* 53 */
	oChangeChoiceClass,				/* 54 */
	oChooseChoiceClass,				/* 55 */
	oVerifyChoiceSymbolLabel,			/* 56 */
	oEnterChoiceSymbolLabel,			/* 57 */
	oxEnterChoiceMerge,				/* 58 */
	oResolveChoiceMerges,				/* 59 */
	oEmitChoiceTable,				/* 60 */
	oxResolveChoiceTableAddress,			/* 61 */
	oEmitFirstChoiceValue,				/* 62 */
	oxEmitFirstChoiceAddress,			/* 63 */
	oStartRules,					/* 64 */
	oBeginRule,					/* 65 */
	oSaveRule,					/* 66 */
	oEndRules,					/* 67 */
	oGenerateDefinitions,				/* 68 */
	oGenerateTable,					/* 69 */
	oEmitValue,					/* 70 */
	oEmitNullAddress				/* 71 */
} TableOperation;
#endif	/* SELFCONTAINED */

/* S/SL Table Size */
#define	sslTableSize	30000	/* Maximum */

/* The S/SL Rule Call Stack Size */
#define	sslStackSize	127

#ifdef	SELFCONTAINED
/* Operation Result Values */
typedef enum {
	invalid,
	valid
} Validity;
#endif	/* SELFCONTAINED */

/* Maximum Source Lines */
#define	maxLineNumber	9999

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

#ifdef	SELFCONTAINED
/* Error Signal Codes */
typedef enum {
	eNoError = 0,				/* 0 */
	eSyntaxError,				/* 1 */
	ePrematureEndOfFile,			/* 2 */
	eExtraneousProgramText,			/* 3 */

/* Semantic Errors */
	eCycleHasNoExits = 10,			/* 10 */
	eDuplicateLabel,			/* 11 */
	eExitNotInCycle,			/* 12 */
	eIllegalParameterClass,			/* 13 */
	eIllegalResultClass,			/* 14 */
	eIllegalNonvaluedReturn,		/* 15 */
	eIllegalStringSynonym,			/* 16 */
	eIllegalValuedReturn,			/* 17 */
	eSymbolPreviouslyDefined,		/* 18 */
	eUndefinedSymbol,			/* 19 */
	eWrongDeclaredResultClass,		/* 20 */
	eWrongLabelClass,			/* 21 */
	eWrongParameterClass,			/* 22 */
	eWrongResultClass,			/* 23 */
	eWrongSymbolClass,			/* 24 */

/* Non-S/SL Semantic Errors */
	eUnresolvedRule = 30,			/* 30 */
	eSymbolTooLong,				/* 31 */
	eNumberTooLarge,			/* 32 */
	eStringTooLong,				/* 33 */
	eValueOutOfRange,			/* 34 */
	eJumpOutOfRange,			/* 35 */

/* Fatal Errors */
	eSslStackOverflow = 40,			/* 40 */
	eCallStackOverflow,			/* 41 */
	eTooManyTotalSymbolChars,		/* 42 */
	eTooManySymbols,			/* 43 */
	eTableTooLarge,				/* 44 */
	eCyclesTooDeep,				/* 45 */
	eTooManyExits,				/* 46 */
	eChoicesTooDeep,			/* 47 */
	eTooManyLabels,				/* 48 */
	eTooManyMerges,				/* 49 */
	eTooManyCalls,				/* 50 */
	eRuleTooLarge,				/* 51 */
} ErrorCodes;			/* Error Code Type */
#endif	/* SELFCONTAINED */

ErrorCodes firstFatalErrorCode = eSslStackOverflow;

/* Maximum Error Count */
#define	maxErrors	100

#ifdef	SELFCONTAINED
/* Input Tokens */
typedef enum {
/* Nonexistent input token used only in syntax error recovery */
	tSyntaxError = -1,			/* -1 */

/* Compound Input Tokens */
	tIdent = 0,				/* 0 */
	tString,				/* 1 */
	tInteger,				/* 2 */

/* Non-Compound Input Tokens */
	tColon,					/* 3 */
	tSemicolon,				/* 4 */
	tEqual,					/* 5 */
	tQuestionMark,				/* 6 */
	tPeriod,				/* 7 */
	tErrorSignal,				/* 8 */
	tCall,					/* 9 */
	tExit,					/* 10 */
	tReturn,				/* 11 */
	tLeftParen,				/* 12 */
	tRightParen,				/* 13 */
	tCycle,					/* 14 */
	tCycleEnd,				/* 15 */
	tChoice,				/* 16 */
	tChoiceEnd,				/* 17 */
	tComma,					/* 18 */
	tOr,					/* 19 */
	tOtherwise,				/* 20 */
	tInput,					/* 21 */
	tOutput,				/* 22 */
	tError,					/* 23 */
	tType,					/* 24 */
	tMechanism,				/* 25 */
	tRules,					/* 26 */
	tEnd,					/* 27 */
	tNewLine,				/* 28 */
	tEndOfFile,				/* 29 */

/* Special token returned by Input Scanner for garbage */
	tIllegal				/* 30 */
} InputTokens;			/* Input Token Type */
#endif	/* SELFCONTAINED */

InputTokens firstCompoundToken = tIdent;
InputTokens lastCompoundToken = tInteger;
InputTokens firstInputToken = tSyntaxError;
InputTokens lastInputToken = tIllegal;

/* Input Scanner Limits and Parameters */
#define	maxInputTokenLength	100	/* must be <= 127 */
#define	maxIdentLength		maxInputTokenLength
#define	maxStringLiteralLength	maxIdentLength
#define	maxNumberLength		5

/* Character Constants */
#define	newline			'\n'
#define	tab			'\t'
#define	blank			' '
#define	quote			'\''
#define	breakChar		'_'

#ifdef	SELFCONTAINED
/*
 * Output Tokens for the S/SL Processor,
 * these are the primitive operations of S/SL 
 */

typedef enum {
	aCall = oCall,
	aReturn = oReturn,
	aRuleEnd = oRuleEnd,
	aJump = oJump,
	aInput = oInput,
	aInputAny = oInputAny,
	aInputChoice = oInputChoice,
	aEmit = oEmit,
	aError = oError,
	aChoice = oChoice,
	aChoiceEnd = oChoiceEnd,
	aSetParameter = oSetParameter,
	aSetResult = oSetResult,
	aSetResultFromInput = oSetResultFromInput
} OutputTokens;			/* Output Token Type */
#endif	/* SELFCONTAINED */


/* Limits on the Assembled Output S/SL Table */
#define	maxOutputTableSize	30000
#define	nullAddress		-7777

/*
 * Limits and Constants Associated with the
 * Semantic Mechanisms of the S/SL Processor 
 */

/* The Symbol Table */

#ifdef	SELFCONTAINED
/* Classes of Symbols */
typedef enum {
	cNotFound = 0,			/* 0 */
	cInput,				/* 1 */
	cOutput,			/* 2 */
	cInputOutput,			/* 3 */
	cError,				/* 4 */
	cType,				/* 5 */
	cMechanism,			/* 6 */
	cUpdateOp,			/* 7 */
	cParmUpdateOp,			/* 8 */
	cChoiceOp,			/* 9 */
	cParmChoiceOp,			/* 10 */
	cRule,				/* 11 */
	cChoiceRule,			/* 12 */
	maxClasses = 50			/* 50 */
} SymbolClass;
#endif	/* SELFCONTAINED */

SymbolClass firstTypeClass = (SymbolClass)(((int)cChoiceRule) + 1);

/* Symbol Table Limits */
#define	maxSymbolChars	30000	/* Total length of all identifiers */
#define	maxSymbols	1500

/* Symbol index of nonexistent symbol */
#define	notFound	0

/* First values for classes */
ErrorCodes firstUserErrorSignalValue = eCycleHasNoExits;
TableOperation firstUserSemanticOperationValue = oEnterCall;

/* Undefined value  */
#ifdef	SELFCONTAINED
typedef enum {
	zero = 0,
	undefined = -9999
} Integer;
#endif	/* SELFCONTAINED */

#define	nullValue	(int)undefined

/* The Call Table */
#define	maxCalls	2000
#define	maxCallsPlusOne	(maxCalls+1)

/* The Cycle Stack */
#define	maxExits	30	/* Pending resolution */
#define	maxCycles	15	/* Deep */

/* The Choice Stack */
#define	maxMerges	100	/* Pending resolution */
#define	maxLabels	100	/* Active */
#define	maxChoices	15	/* Deep */


/* Assembled S/SL Table Index */
typedef u_int OutputAddress;	/* 0 .. maxOutputTableSize */

/* Types Used in the Semantic Mechanisms of the S/SL Processor */

/* The Symbol Table */
typedef int SymbolIndex;	/* 0 .. maxSymbols */


/* Standard Characters */
#define	newpage		'\f'
#define	endfile		-1

/* The Syntax/Semantic Table */

typedef int boolean;
typedef int sslPointerRange;	/* 0 .. sslTableSize */

/* Table Walker State */
boolean processing = true;
sslPointerRange sslPointer = 0;
TableOperation operation;

/* Abort Flag */
boolean abortflag = false;

/*
 * The S/SL Rule Call Stack:
 * 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.		
 */

typedef int sslTableIndex;	/* 0 .. sslTableSize */	/*X: 1 base in turing */
typedef int sslStackIndex;	/* 0 .. sslStackSize */

sslTableIndex sslStack[sslStackSize];
sslStackIndex sslTop = 0;

/*
 * Choice Match Flag:
 * 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.							
 */

boolean choiceTagMatched;

/*
 * Parameterized And Choice Semantic Operation Values:
 * 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.					 
 */

int parameterValue;
int resultValue;

/* Line Counters */
int nextLineNumber = 0;
int lineNumber;

/* Error Counter */
int noErrors = 0;

typedef char InputText[maxInputTokenLength];

/* Input Interface */
InputTokens nextToken;
int	nextTokenValue;
InputText	nextTokenText;

/*
 * 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.		
 */
InputTokens compoundToken;	/* Last compound input token accepted */
int	compoundValue;		/* Its associated value */
InputText	compoundText;

/* Variables Used in Syntax Error Recovery */
boolean newInputLine = false;
InputTokens savedToken;

/* Input Scanner Interface and Tables */

/* Lookahead Character */
int	nextChar;

/* Keyword Token Table */
const char *keywordText[] = { "mechanism", "output", "input", "error", "rules",
			      "type", "end", "do", "od", "if", "fi"
};

InputTokens keywordToken[] = { tMechanism, tOutput, tInput, tError, tRules,
			       tType, tEnd, tCycle, tCycleEnd, tChoice,
			       tChoiceEnd
};

/* Special Symbol Token Table */
int XspecialChar[(int)tIllegal-(int)tSyntaxError+1];
int *specialChar=XspecialChar-(int)tSyntaxError;

/* First read flag */
boolean firstChar = true;

/* The Assembled Table */
int   outputTable[maxOutputTableSize];
const char *outputNameTable[maxOutputTableSize];
OutputAddress outputPointer = 0;

/* The Semantic Mechanism Data Structures of the S/SL Processor */

/* The Symbol Table */
SymbolClass symClass[maxSymbols];
const char *symText[maxSymbols];
int symHash[maxSymbols];
int symValue[maxSymbols];
SymbolClass symParmClass[maxSymbols];
SymbolClass symResultClass[maxSymbols];
SymbolIndex symTop = 0;

/*
 * symIndex is the index in the Symbol Table
 * of the symbol last referenced  symIndex = 0 (notFound)
 * indicates the referenced symbol is not present in the table. 
 */
SymbolIndex symIndex = 0;

/*
 * savedSymIndex is used to save the
 * the value of symIndex for later recall 
 */
SymbolIndex savedSymIndex;

/*
 * enclosingSymIndex is used to save the symbol index of the
 * enclosing S/SL rule or type for later recall			
 */
SymbolIndex enclosingSymIndex;

/* Next Symbol Value */
int symNextValue[(int)maxClasses];

/* Current Definition Class */
SymbolClass symCurrentClass = cNotFound;

/* The Call Table */

OutputAddress callAddress[maxCalls];

/*
 * callRule is the SymbolIndex of the called rule and
 * is later resolved to an OutputAddress			
 */
int callRule[maxCalls];
u_int callTop = 0;		/* 0 .. maxCalls */

/* The Cycle Stack */

/* The Cycle Exit Stack */
OutputAddress exitAddress[maxExits];
u_int exitTop = 0;		/* 0 .. maxExits */

/* The Cycle Stack */
OutputAddress cycleAddress[maxCycles];

/*
 * cycleExitIndex is the origin of the
 * portion of the Exit Stack for the cycle 
 */
u_int cycleExitIndex[maxCycles];
u_int cycleTop = 0;

/* The Choice Stack */

/*
 * The Choice Merge Stack:  used to save the addresses
 * of the merge branches following each alternative of a choice 
 */
OutputAddress mergeAddress[maxMerges];
u_int mergeTop = 0;

/*
 * The Choice Label Stack:  used to save the alternative
 * values and corresponding table addresses in a choice 
 */
int labelValue[maxLabels];
OutputAddress labelAddress[maxLabels];
u_int labelTop = 0;

/* The Choice Stack */
SymbolClass choiceClass[maxChoices];
OutputAddress choiceAddress[maxChoices];

/*
 * choiceMergeIndex is the origin of the portion
 * of the Merge Stack for the choice		
 */
u_int choiceMergeIndex[maxChoices];

/*
 * choiceLabelIndex is the origin of the portion
 * of the Label Stack for the choice		
 */
u_int choiceLabelIndex[maxChoices];
u_int choiceTop = 0;

/* Input/Output Streams */

FILE *sourcefp;
FILE *headerfp;
FILE *tablefp;
FILE *listfp;
FILE *entryfp;

char *progname;

#ifndef __
#ifdef __STDC__
# define __(x) x
#else
# define __(x) ()
#endif
#endif

extern char *ssl_basename __((char *)); 
extern char *strsave __((char *));

extern Validity VerifyChoiceLabel __((int));
extern void AcceptInputToken __((void));
extern void ChangeChoiceClass __((SymbolClass));
extern void ChangeSymbolClass __((void));
static void Emit __((const int value, const char *name));
extern void EmitChoiceTable __((void));
extern void EmitCycleAddress __((void));
extern void EmitFirstChoiceAddress __((void));
extern void EmitFixup __((OutputAddress, int, const char *));
extern void EmitJumpAddress __((OutputAddress));
extern void EmitJumpFixup __((OutputAddress));
extern void EmitNullAddress __((void));
extern void EmitNullCallAddress __((void));
extern void EnterCall __((void));
extern void EnterChoiceMerge __((void));
extern void EnterCycleExit __((void));
extern void EnterNewSymbol __((void));
extern void EnterSecondNewSymbolValue __((void));
extern void EnterSymbolValueFromAddress __((void));
extern void Error __((ErrorCodes));
extern void EvaluateNumber __((void));
extern void GenerateClass __((SymbolClass, const char *));
extern void GenerateEntryPoints __((const char *));
extern void GenerateOutputTable __((const char *));
extern void GenerateSymbolDefinitions __((void));
extern void IncrementCurrentClassValue __((void));
extern void InitCallTable __((void));
extern void InitChoiceStack __((void));
extern void InitCycleStack __((void));
extern void InitInputScanner __((void));
extern void InitSymbolTable __((void));
extern void LookupKeyword __((void));
extern void LookupSymbol __((void));
extern void PopChoice __((void));
extern void PopCycle __((void));
extern void PushChoice __((SymbolClass));
extern void PushCycle __((void));
extern void ResolveCalls __((void));
extern void ResolveChoiceMerges __((void));
extern void ResolveCycleExits __((void));
extern void RestoreCurrentSymbol __((void));
extern void RestoreEnclosingSymbol __((void));
extern void SaveCurrentSymbol __((void));
extern void SaveEnclosingSymbol __((void));
extern void SetCurrentSymbol __((SymbolIndex));
extern void SetNextValueOfCurrentClass __((int));
extern void SslChoice __((int));
extern void SslFailure __((FailureCodes));
extern void SslGenerateCompoundInputToken __((InputTokens));
extern void SslSyntaxError __((void));
extern void SslTrace __((void));
extern int  hash __((const char *));
extern void xEmitFirstChoiceValue __((void));
extern void xEnterChoiceLabel __((int));
extern void xEnterNewSymbolValue __((void));
extern void xEnterSymbolParameterClass __((void));
extern void xResolveChoiceTableAddress __((void));
extern void xSetCurrentClass __((SymbolClass));
extern void yEnterSymbolResultClass __((void));

boolean tracing;
boolean summarize;

extern int main __((int argc, char *argv[]));

int
main(argc, argv)
	int argc;
	char *argv[];
{
	SymbolClass c, d;
	int ch, errflag;
	int v;
	const char *n;
	boolean listing;
	char buf[1024 /* PATH_MAX */];
	const char *useEnum, *typeName;
	char *inFile, *tableFile, *headerFile, *listFile, *entryFile;

	progname = argv[0];
	/* Get Run Options */

	/* Default no listing, tracing, summary, Turing tables */
	tracing = false;
	summarize = false;
	listing = false;
	errflag = 0;
	inFile = tableFile = headerFile = listFile = entryFile = NULL;
	typeName = useEnum = NULL;
	sourcefp = tablefp = headerfp = listfp = entryfp = NULL;

	while ((ch = getopt(argc, argv, "TLsi:h:t:l:e:E:D:")) != EOF) {
		switch (ch) {
		case 'T':
			tracing = true;
			break;
		case 'L':
			listing = true;
			break;
		case 'D':
			if (*optarg != '\0')
				typeName = optarg;
			else
				++errflag;
			break;
		case 'E':
			if (*optarg != '\0')
				useEnum = optarg;
			else
				++errflag;
			break;
		case 's':
			summarize = true;
			break;
		case 'i':	/* input S/SL code file */
			if (*optarg != '\0')
				inFile = optarg;
			else
				++errflag;
			break;
		case 'h':	/* output C header file */
			if (*optarg != '\0')
				headerFile = optarg;
			else
				++errflag;
			break;
		case 't':	/* output C S/SL table file */
			if (*optarg != '\0')
				tableFile = optarg;
			else
				++errflag;
			break;
		case 'l':	/* output listing file */
			if (*optarg != '\0')
				listFile = optarg;
			else
				++errflag;
			break;
		case 'e':	/* entry points saved here */
			if (*optarg != '\0')
				entryFile = optarg;
			else
				++errflag;
			break;
		default:
			++errflag;
			break;
		}
	}
	if (errflag || argc < optind + 1) {
		fprintf(stderr,
			       "Usage: %s [-TLs] [-E Name] [-D type] [-h file.sst.h] [-t file.sst.c] [-l file.lst] [-e file.entry] [-i] file.ssl\n",
			       progname);
		exit(1);
	}


	/* Input/Output File Assignments */
	if (inFile == NULL && optind >= argc)
		sourcefp = stdin;
	else {
		if (inFile == NULL)
			inFile = argv[optind++];
		if ((sourcefp = fopen(inFile, "r")) == NULL) {
			fprintf(stderr, "%s: cannot open '%s'\n",
					progname, inFile);
			exit(1);
		}
	}
	if (inFile != NULL && headerFile == NULL)
		sprintf(headerFile = buf, "%s.sst.h", ssl_basename(inFile));
	if (headerFile != NULL
	    && (headerfp = fopen(headerFile, "w")) == NULL) {
		fprintf(stderr, "%s: cannot open '%s'\n", progname, headerFile);
		exit(1);
	}
	if (inFile != NULL && tableFile == NULL)
		sprintf(tableFile = buf, "%s.sst.c", ssl_basename(inFile));
	if (tableFile != NULL
	    && (tablefp = fopen(tableFile, "w")) == NULL) {
		fprintf(stderr, "%s: cannot open '%s'\n", progname, tableFile);
		exit(1);
	}
	if (inFile != NULL && listFile == NULL && listing)
		sprintf(listFile = buf, "%s.lst", ssl_basename(inFile));
	if (listFile != NULL
	    && (listfp = fopen(listFile, "w")) == NULL) {
		fprintf(stderr, "%s: cannot open '%s'\n", progname, listFile);
		exit(1);
	}
	if (inFile != NULL && entryFile == NULL)
		sprintf(entryFile = buf, "%s.entry", ssl_basename(inFile));
	if (entryFile != NULL
	    && (entryfp = fopen(entryFile, "w")) == NULL) {
		fprintf(stderr, "%s: cannot open '%s'\n", progname, entryFile);
		exit(1);
	}

	/* Initialize Table Walker State */
	processing = true;
	sslPointer = 0;
	sslTop = 0;
	noErrors = 0;
	abortflag = false;

	/* Initialize Semantic Mechanisms */
	InitSymbolTable();
	InitCallTable();
	InitCycleStack();
	InitChoiceStack();

	/* Initialize Output */
	outputPointer = 0;

	/* Initialize Input */
	InitInputScanner();
	nextToken = tNewLine;
	nextLineNumber = 0;
	newInputLine = false;
	AcceptInputToken();

	/* Walk the S/SL Table */

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

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

		switch (operation) {
		case oCall:
			if (sslTop < sslStackSize) {
				sslStack[++sslTop] = sslPointer + 1;
				sslPointer = sslTable[sslPointer];
			} else {
				Error(eSslStackOverflow);
				processing = false;
			}
			break;
		case oReturn:
			if (sslTop == 0)
				/* Return from main S/SL rule */
				processing = false;
			else
				sslPointer = sslStack[sslTop--];
			break;
		case oRuleEnd:
			SslFailure(fChoiceRuleFailed);
			break;
		case oJump:
			sslPointer = sslTable[sslPointer];
			break;
		case oInput:
			if (sslTable[sslPointer] == (int)nextToken)
				AcceptInputToken();
			else
				/* Syntax error in input */
				SslSyntaxError();

			++sslPointer;
			break;
		case oInputAny:
			if (nextToken != tEndOfFile)
				AcceptInputToken();
			else
				/* Premature end of file */
				SslSyntaxError();
			break;
		case oInputChoice:
			SslChoice(nextToken);

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

		/* Semantic Operations of the S/SL Processor */

		/* Call Table Mechanism Operations */
		case oEnterCall:
			EnterCall();
			break;
		case oEmitNullCallAddress:
			EmitNullCallAddress();
			break;
		case oResolveCalls:
			ResolveCalls();
			break;

		/* Symbol Table Mechanism Operations */
		case oSetClass:
			xSetCurrentClass((SymbolClass)parameterValue);
			break;
		case oSetClassFromSymbolClass:
			xSymbolClass(c);
			xSetCurrentClass(c);
			break;
		case oxSetClassFromSymbolValue:
			SymbolValue(v);
			xSetCurrentClass((SymbolClass)v);
			break;
		case oySetClassFromSymbolResultClass:
			SymbolResultClass(c);
			xSetCurrentClass(c);
			break;
		case ozSetClassFromSymbolParameterClass:
			SymbolParameterClass(c);
			xSetCurrentClass(c);
			break;
		case ovSetClassFromChoiceClass:
			ClassOfChoice(c);
			xSetCurrentClass(c);
			break;
		case oChooseClass:
			CurrentClass(c);
			resultValue = (int)c;
			break;
		case oSetClassValue:
			SetNextValueOfCurrentClass(compoundValue);
			break;
		case owSetClassValueFromSymbolValue:
			SymbolValue(v);
			SetNextValueOfCurrentClass(v);
			break;
		case oIncrementClassValue:
			IncrementCurrentClassValue();
			break;
		case oEnterNewSymbol:
			EnterNewSymbol();
			break;
		case oLookupSymbol:
			LookupSymbol();
			break;
		case oChangeSymbolClass:
			ChangeSymbolClass();
			break;
		case oChooseSymbolClass:
			xSymbolClass(c);
			resultValue = (int)c;
			break;
		case oVerifySymbolClass:
			xSymbolClass(c);
			CurrentClass(d);

			if (c == d)
				resultValue = (int)valid;
			else
				resultValue = (int)invalid;
			break;
		case oxEnterNewSymbolValue:
			xEnterNewSymbolValue();
			break;
		case oEnterSecondNewSymbolValue:
			EnterSecondNewSymbolValue();
			break;
		case oEnterSymbolValueFromAddress:
			EnterSymbolValueFromAddress();
			break;
		case oxChooseSymbolValue:
			SymbolValue(resultValue);
			break;
		case oEmitSymbolValue:
			SymbolValue(v);
			SymbolName(n);
			Emit(v,n);
			break;
		case oxVerifySymbolClassValue:
			SymbolValue(v);
			CurrentClass(c);

			if (v == (int)c)
				resultValue = (int)valid;
			else
				resultValue = (int)invalid;
			break;
		case oxEnterSymbolParameterClass:
			xEnterSymbolParameterClass();
			break;
		case oyEnterSymbolResultClass:
			yEnterSymbolResultClass();
			break;
		case oyChooseSymbolResultClass:
			SymbolResultClass(c);
			resultValue = (int)c;
			break;
		case oSaveEnclosingSymbol:
			SaveEnclosingSymbol();
			break;
		case oRestoreEnclosingSymbol:
			RestoreEnclosingSymbol();
			break;
		case oSaveCurrentSymbol:
			SaveCurrentSymbol();
			break;
		case oRestoreCurrentSymbol:
			RestoreCurrentSymbol();
			break;
		/* Cycle Stack Mechanism Operations */
		case oPushCycle:
			PushCycle();
			break;
		case oPopCycle:
			PopCycle();
			break;
		case oChooseCycleDepth:
			CycleDepth(resultValue);
			break;
		case oEmitCycleAddress:
			EmitCycleAddress();
			break;
		case oEnterCycleExit:
			EnterCycleExit();
			break;
		case oResolveCycleExits:
			ResolveCycleExits();
			break;
		case oxChooseCycleExits:
			CycleExits(resultValue);
			break;
		/* Choice Stack Mechanism Operations */
		case oPushChoice:
			CurrentClass(c);
			PushChoice(c);
			break;
		case oPopChoice:
			PopChoice();
			break;
		case oChangeChoiceClass:
			CurrentClass(c);
			ChangeChoiceClass(c);
			break;
		case oChooseChoiceClass:
			ClassOfChoice(c);
			resultValue = (int)c;
			break;
		case oVerifyChoiceSymbolLabel:
			SymbolValue(v);
			resultValue = (int)VerifyChoiceLabel(v);
			break;
		case oEnterChoiceSymbolLabel:
			SymbolValue(v);
			xEnterChoiceLabel(v);
			break;
		case oxEnterChoiceMerge:
			EnterChoiceMerge();
			break;
		case oResolveChoiceMerges:
			ResolveChoiceMerges();
			break;
		case oEmitChoiceTable:
			EmitChoiceTable();
			break;
		case oxResolveChoiceTableAddress:
			xResolveChoiceTableAddress();
			break;
		case oEmitFirstChoiceValue:
			xEmitFirstChoiceValue();
			break;
		case oxEmitFirstChoiceAddress:
			EmitFirstChoiceAddress();
			break;
		/* Rule Table Operations */
		case oStartRules:
			StartRules();
			break;
		case oBeginRule:
			BeginRule();
			break;
		case oSaveRule:
			SaveRule();
			break;
		case oEndRules:
			EndRules();
			break;
		/* Generate Output Operations */
		case oGenerateDefinitions:
			GenerateSymbolDefinitions();
			break;
		case oGenerateTable:
			GenerateOutputTable(typeName);
			GenerateEntryPoints(useEnum);
			break;
		/* Miscellaneous Output Operations */
		case oEmitValue:
			Emit(compoundValue,strsave(compoundText));
			break;
		case oEmitNullAddress:
			EmitNullAddress();
			break;
		default:
			break;
		}
	}

	if (nextToken != tEndOfFile && !abortflag)
		Error(eExtraneousProgramText);

	/* Summarize Table Usage if Requested */
	if (summarize)
		printf("%d/%d table entries, %d/%d symbols, %d/%d calls.\n",
		       outputPointer+1, maxOutputTableSize,
		       symTop, maxSymbols, callTop, maxCalls);
	exit(0);
}

char *
ssl_basename(s)
	char *s;
{
	char *cp;

	if (strlen(s) > 4 && strcmp(s + strlen(s) - 4, ".ssl") == 0) {
		cp = strsave(s);
		*(cp+strlen(cp)-4) = '\0';
		return cp;
	}
	return s;
}

char *
strsave(s)
	char *s;
{
	char *cp;

	cp = (char*)malloc(strlen(s)+1);
	strcpy(cp, s);
	return cp;
}

int
hash(s)
	register const char *s;
{
	int i;

	for (i = 1; *s != '\0'; ++s, i>>=1)
		i *= *s;
	return i;
}

extern char *LowerCase __((const char *s));

char *
LowerCase(s)
	register const char *s;
{
	static char buf[BUFSIZ];
	char *cp;

	for (cp = buf; *s != '\0'; ++s, ++cp) {
		int c = (*s) & 0xFF;
		if (isascii(c) && isupper(c))
			*cp = tolower(c);
		else
			*cp = c;
	}
	*cp = '\0';
	return buf;
}


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

void
Error(errCode)
	ErrorCodes errCode;
{
	if (errCode == eNoError)
		assertionFailure;

	printf("Line ");

	if (errCode == eSyntaxError) {
		/* Syntax errors are in the lookahead token */
		printf("%d", nextLineNumber);
	} else {
		/* Semantic errors are in the accepted token */
		printf("%d", lineNumber);
	}

	printf(": ");

	switch (errCode) {
	case eSyntaxError:
		printf("Syntax error at '%s'\n", nextTokenText);
		break;
	case ePrematureEndOfFile:
		printf("Unexpected end of file\n");
		break;
	case eExtraneousProgramText:
		printf("Extraneous program text\n");
		break;
	case eSymbolTooLong:
		printf("Symbol too long\n");
		break;
	case eNumberTooLarge:
		printf("Integer value too large (or small)\n");
		break;
	case eStringTooLong:
		printf("String too long\n");
		break;
	case eUndefinedSymbol:
		printf("Symbol '%s' undefined\n", compoundText);
		break;
	case eSymbolPreviouslyDefined:
		printf("Symbol '%s' previously defined\n", compoundText);
		break;
	case eWrongSymbolClass:
		printf("Illegal context for symbol '%s'\n", compoundText);
		break;
	case eUnresolvedRule:
		printf("Rule '%s' undefined\n", compoundText);
		break;
	case eValueOutOfRange:
		printf("Symbol value not in table value range\n");
		break;
	case eJumpOutOfRange:
		printf("Jump distance exceeds table value range\n");
		break;
	case eIllegalStringSynonym:
		printf("Illegal string synonym\n");
		break;
	case eTooManyTotalSymbolChars:
		printf("Too many symbols (chars)\n");
		break;
	case eTooManySymbols:
		printf("Too many symbols\n");
		break;
	case eTableTooLarge:
		printf("Table too large\n");
		break;
	case eRuleTooLarge:
		printf("Rule too large\n");
		break;
	case eTooManyCalls:
		printf("Too many rule calls\n");
		break;
	case eCyclesTooDeep:
		printf("Cycles too deep\n");
		break;
	case eChoicesTooDeep:
		printf("Choices too deep\n");
		break;
	case eTooManyExits:
		printf("Too many cycle exits\n");
		break;
	case eTooManyLabels:
	case eTooManyMerges:
		printf("Too many alternatives\n");
		break;
	case eCycleHasNoExits:
		printf("(Warning) Cycle does not contain a cycle exit\n");
		break;
	case eExitNotInCycle:
		printf("Cycle exit not in cycle\n");
		break;
	case eDuplicateLabel:
		printf("Duplicate alternative label\n");
		break;
	case eIllegalParameterClass:
	case eIllegalResultClass:
		printf("Type name required as parameter or result type\n");
		break;
	case eIllegalNonvaluedReturn:
		printf("Non-valued return in choice rule\n");
		break;
	case eIllegalValuedReturn:
		printf("Valued return in procedure rule\n");
		break;
	case eWrongLabelClass:
		printf("Alternative label is wrong type\n");
		break;
	case eWrongParameterClass:
		printf("Parameter is wrong type\n");
		break;
	case eWrongDeclaredResultClass:
		printf("Result type does not match previous use\n");
		break;
	case eWrongResultClass:
		printf("Result value is wrong type\n");
		break;
	default:
		break;
	}

	++noErrors;

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

extern void ReadNextChar __((void));
void
ReadNextChar()
{
	if (listfp) {
		if (firstChar)
			firstChar = false;
		else if (nextChar == newline)
			putc('\n', listfp);
		else if (nextChar != endfile)
			putc(nextChar, listfp);

		if (nextChar == newline) {
			nextChar = getc(sourcefp);
			if (nextChar != EOF)
				fprintf(listfp, "%4d\t", outputPointer);
			return;
		}
	}

	nextChar = getc(sourcefp);
}


void
EvaluateNumber()
{
	if (nextToken != tInteger || nextTokenText[0] == '\0' ||
	    (strlen(nextTokenText) > maxNumberLength &&
	     (nextTokenText[0] != '-' ||
	      strlen(nextTokenText) > maxNumberLength+1)))
		assertionFailure;

	nextTokenValue = atoi(nextTokenText);
}


void
LookupKeyword()
{
	char *nt;
	u_int i;

	if (nextToken != tIdent || nextTokenText[0] == '\0')
		assertionFailure;

	nt = LowerCase(nextTokenText);

	/* The keyword table is ordered by length, longest first */

	if (strlen(nt) <= strlen(keywordText[0])) /* the longest */ {
		for (i=0; i < (sizeof keywordText)/(sizeof keywordText[0]); ++i)
			if (strcmp(nt, keywordText[i]) == 0) {
				nextToken = keywordToken[i];
				return;
			}
	}
}

extern void GetNextInputToken __((void));
void
GetNextInputToken()
{
	InputTokens t;
	ErrorCodes errCode;
	int i;

	if (maxInputTokenLength < maxIdentLength ||
	    maxInputTokenLength < maxStringLiteralLength ||
	    maxInputTokenLength < maxNumberLength)
		assertionFailure;

	errCode = eNoError;

	/* Skip blanks and comments */
	while (nextChar == blank || nextChar == tab
	       || nextChar == newpage || nextChar == '%') {
		if (nextChar == '%') {	/* Skip comment */
			do
				ReadNextChar();
			while (nextChar != newline && nextChar != endfile);
		} else
			ReadNextChar();
	}

	/* Scan and set nextToken */
	i = 0;
	nextTokenText[0] = '\0';
	nextTokenValue = 0;

	if (isascii(nextChar) && isalpha(nextChar)) {
		/* Scan identifier */
		do {
			if (i < maxIdentLength)
				nextTokenText[i++] = nextChar;
			else
				errCode = eSymbolTooLong;
			ReadNextChar();
		} while (isalpha(nextChar) && isalnum(nextChar));

		nextToken = tIdent;

		/* Test for Keyword */
		nextTokenText[i] = '\0';
		LookupKeyword();

	} else if (nextChar=='-' || (isascii(nextChar) && isdigit(nextChar))) {
		/* Scan number */
		if (nextChar == '-') {
			nextTokenText[i++] = nextChar;
			ReadNextChar();
		}

		if (maxInputTokenLength <= maxNumberLength+1)
			assertionFailure;

		do {
			if (i < maxNumberLength+2)
				nextTokenText[i++] = nextChar;

			ReadNextChar();
		} while (isascii(nextChar) && isdigit(nextChar));

		nextToken = tInteger;

		if (i > maxNumberLength+1
		    || (nextTokenText[0] != '-' && i > maxNumberLength)) {
			errCode = eNumberTooLarge;
			nextTokenValue = 0;
		} else {
			nextTokenText[i] = '\0';
			EvaluateNumber();
		}

	} else if (nextChar == quote) {
		/* Scan String */
		nextChar = '"';
		do {
			if (i < maxStringLiteralLength-1)
				nextTokenText[i++] = nextChar;
			else
				errCode = eStringTooLong;

			ReadNextChar();
		} while (nextChar != quote
			 && nextChar != newline && nextChar != endfile);

		nextTokenText[i++] = /*quote*/ '"';
		nextTokenText[i] = '\0';

		if (nextChar == quote)
			ReadNextChar();

		nextToken = tString;

	} else {
		/* Special Symbols */
		if (lastInputToken != tIllegal)
			assertionFailure;
		for (t = firstInputToken; t != lastInputToken;
		     t = (InputTokens)((int)t + 1))
			if (specialChar[(int)t] == nextChar)
				break;

		nextToken = t;
		nextTokenText[0] = nextChar;
		nextTokenText[1] = '\0';
		ReadNextChar();

		if (nextToken == tExit && nextChar == '>') {
			nextToken = tReturn;
			ReadNextChar();
		} else if (nextToken == tIllegal && nextTokenText[0] == '!') {
			/* Alternate for tOr */
			nextToken = tOr;
		}
		if (nextToken == tIllegal) /* XX: fix this later */
			abort();
	}

	if (errCode != eNoError)
		Error(errCode);
}


void
InitInputScanner()
{
	InputTokens t;

	/* Initialize Special Character Table */
	for (t = firstInputToken; ((int)t) <= ((int)lastInputToken);
	     t = (InputTokens)((int)t + 1))
		specialChar[(int)t] = blank;

	specialChar[(int)tColon] = ':';
	specialChar[(int)tSemicolon] = ';';
	specialChar[(int)tEqual] = '=';
	specialChar[(int)tQuestionMark] = '?';
	specialChar[(int)tPeriod] = '.';
	specialChar[(int)tErrorSignal] = '#';
	specialChar[(int)tCall] = '@';
	specialChar[(int)tExit] = '>';
	specialChar[(int)tReturn] = blank;	/* tReturn handled specially */
	specialChar[(int)tLeftParen] = '(';
	specialChar[(int)tRightParen] = ')';
	specialChar[(int)tCycle] = '{';
	specialChar[(int)tCycleEnd] = '}';
	specialChar[(int)tChoice] = '[';
	specialChar[(int)tChoiceEnd] = ']';
	specialChar[(int)tComma] = ',';
	specialChar[(int)tOr] = '|';	/* alternate '!' handled specially */
	specialChar[(int)tOtherwise] = '*';
	specialChar[(int)tNewLine] = newline;
	specialChar[(int)tEndOfFile] = endfile;
	specialChar[(int)tIllegal] = blank;	/* tIllegal handled specially */

	/* Initialize Lookahead */
	nextChar = newline;
	firstChar = true;
}


void
AcceptInputToken()
{
	InputTokens acceptedToken;

	if (nextToken == tEndOfFile)
		assertionFailure;

	/* Accept Token */
	acceptedToken = nextToken;

	if (acceptedToken == tIdent
	    || acceptedToken == tString || acceptedToken == tInteger) {
		compoundToken = acceptedToken;
		strcpy(compoundText, nextTokenText);
		compoundValue = nextTokenValue;
	}

	/* Update Line Number */
	lineNumber = nextLineNumber;

	/* Get Next Input Token */
	newInputLine = false;
	do {
		GetNextInputToken();

		if (nextToken == tNewLine) {
			/* Update Line Counter and Set Flag */
			newInputLine = true;

			if (nextLineNumber < maxLineNumber)
				++nextLineNumber;
			else
				nextLineNumber = 0;
		}
	} while (nextToken == tNewLine);


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


/* Emit an output table element to the assembled table */

static void
Emit(value,name)
const int value;
const char *name;
{
	if (name && *name == '?')
	  name = NULL;
	if (outputPointer < maxOutputTableSize) {
		outputTable[outputPointer] = value;
		outputNameTable[outputPointer] = name;
		++outputPointer;

		/* Trace Table Assembly */
		if (tracing)
			printf("Output emitted at %d: %d (%s)\n",
				      outputPointer-1, value, name);
	} else
		Error(eTableTooLarge);
}


/* Fixup a previously emitted table location to a resolved value */

void
EmitFixup(fixupAddress, value, name)
	OutputAddress fixupAddress;
	int value;
	const char *name;
{
	if (fixupAddress >= outputPointer
	    || outputTable[fixupAddress] != nullAddress)
		assertionFailure;

	outputTable[fixupAddress] = value;
	outputNameTable[fixupAddress] = NULL /*name*/;

	/* Trace Table Assembly */
	if (tracing)
	  printf("Output fixup at %d: %d (%s)\n", fixupAddress, value,name);
}


/* Reserve a table location to be fixed up */
extern void EmitNullAddress __((void));
void
EmitNullAddress()
{
	Emit(nullAddress,NULL);
}


/* Emit a backward jump address.  Jump addresses are absolute. */

void
EmitJumpAddress(targetAddress)
	OutputAddress targetAddress;
{
	Emit((int)targetAddress,NULL);
}


/* Fixup a forward jump address.  Jump addresses are absolute. */

void
EmitJumpFixup(jumpAddress)
	OutputAddress jumpAddress;
{
	EmitFixup(jumpAddress, (int)outputPointer, NULL);
}


void
EmitNullCallAddress()
{
	EmitNullAddress();
}


/*
 * The Symbol Table Mechanism 
 *
 * The Symbol Table is used to keep track of defined symbols
 * in the S/SL program.  It provides facilities to save defined
 * symbols, resolve referenced symbols, and keep track of symbol
 * values and parameter and result types.				
 */

void
EnterNewSymbol()
{

	if ((compoundToken != tIdent && compoundToken != tString)
	    || strlen(compoundText) == 0)
		assertionFailure;

	/* Enter in the Symbol Table */
	if (symTop < maxSymbols) {
		++symTop;
		symText[symTop] = strsave(compoundText);
		symHash[symTop] = hash(LowerCase(compoundText));
		symClass[symTop] = symCurrentClass;
		symValue[symTop] = nullValue;
		symParmClass[symTop] = cNotFound;
		symResultClass[symTop] = cNotFound;
	} else
		Error(eTooManySymbols);

	symIndex = symTop;
}


/* This procedure looks up a symbol (or string) in the symbolTable */

void
LookupSymbol()
{
	char ct[BUFSIZ];
	int h;

	if (compoundToken != tIdent && compoundToken != tString)
		assertionFailure;

	strcpy(ct, LowerCase(compoundText));
	h = hash(ct);

	symIndex = symTop;

	while (symIndex != notFound
	       && (h != symHash[symIndex]
		   || strcmp(ct, LowerCase(symText[symIndex])) != 0))
		--symIndex;

	/*
	 * symIndex is now the index in the Symbol Table of the 
	 * entry for the symbol if present and notFound otherwise 
	 */
}


void
xEnterNewSymbolValue()
{
	if (symTop < 0)
		assertionFailure;

	symValue[symTop] = symNextValue[(int)symCurrentClass];

	if (!isalpha(symText[symTop][0])) return;

	if (symCurrentClass == cUpdateOp
	    && machineOpNames[symNextValue[(int)symCurrentClass]] == NULL)
		machineOpNames[symNextValue[(int)symCurrentClass]] =
							      symText[symTop];
	else if (symCurrentClass == cError
		 && errorCodeNames[symNextValue[(int)symCurrentClass]] == NULL)
		errorCodeNames[symNextValue[(int)symCurrentClass]] =
							      symText[symTop];
}


void
EnterSecondNewSymbolValue()
{
	if (symTop < 2)
		assertionFailure;

	symValue[symTop-1] = symNextValue[(int)symCurrentClass];
}


void
EnterSymbolValueFromAddress()
{
	symValue[symIndex] = outputPointer;
}


void
SetCurrentSymbol(newIndex)
	SymbolIndex newIndex;
{
	symIndex = newIndex;
}


void
xSetCurrentClass(newClass)
	SymbolClass newClass;
{
	int nextOpValue;

	/* Synchronize operation values */
	if (symCurrentClass == cUpdateOp
	    || symCurrentClass == cParmUpdateOp
	    || symCurrentClass == cChoiceOp
	    || symCurrentClass == cParmChoiceOp) {
		nextOpValue = symNextValue[(int)symCurrentClass];
		symNextValue[(int)cUpdateOp] = nextOpValue;
		symNextValue[(int)cParmUpdateOp] = nextOpValue;
		symNextValue[(int)cChoiceOp] = nextOpValue;
		symNextValue[(int)cParmChoiceOp] = nextOpValue;
	}

	/* Synchronize input/output token values */
	if ((symCurrentClass == cInput || symCurrentClass == cOutput) &&
	    symNextValue[(int)symCurrentClass] >symNextValue[(int)cInputOutput])
		symNextValue[(int)cInputOutput] =
			symNextValue[(int)symCurrentClass];

	symCurrentClass = newClass;
}


void
SetNextValueOfCurrentClass(newValue)
	int newValue;
{
	symNextValue[(int)symCurrentClass] = newValue;
}


void
IncrementCurrentClassValue()
{
	symNextValue[(int)symCurrentClass] += 1;
}


extern void CopySymbolTextToTokenBuffer __((void));
void
CopySymbolTextToTokenBuffer()
{
	strcpy(compoundText, symText[symIndex]);
}


void
ChangeSymbolClass()
{
	if (symIndex == notFound)
		assertionFailure;

	symClass[symIndex] = symCurrentClass;
}


void
xEnterSymbolParameterClass()
{
	if (symIndex == notFound)
		assertionFailure;

	symParmClass[symIndex] = symCurrentClass;
}


void
yEnterSymbolResultClass()
{
	if (symIndex == notFound)
		assertionFailure;

	symResultClass[symIndex] = symCurrentClass;
}


void
SaveCurrentSymbol()
{
	savedSymIndex = symIndex;
}


void
RestoreCurrentSymbol()
{
	symIndex = savedSymIndex;
}


void
SaveEnclosingSymbol()
{
	enclosingSymIndex = symIndex;
}


void
RestoreEnclosingSymbol()
{
	symIndex = enclosingSymIndex;
}


void
InitSymbolTable()
{
	SymbolClass i;
	const char **cpp;

	/* Initialize Symbol Table */
	if (notFound != 0)
		assertionFailure;
	symTop = notFound;
	symClass[notFound] = cNotFound;
	symText[notFound] = "?";

	compoundToken = tIdent;
	/* enter the S/SL machine instructions */
	symCurrentClass = cUpdateOp;
	symNextValue[(int)cUpdateOp] = 0;
	for (cpp = &machineOpNames[0]; *cpp != NULL; ++cpp) {
		strcpy(compoundText, *cpp);
		EnterNewSymbol();
		xEnterNewSymbolValue();
		IncrementCurrentClassValue();
	}
	for (;cpp < &machineOpNames[(sizeof(machineOpNames)/sizeof(char *))]; ++cpp)
		*cpp = NULL; /* Blank all the rest of that array */

	/* enter the S/SL error signal codes */
	symCurrentClass = cError;
	symNextValue[(int)cError] = 0;
	for (cpp = &errorCodeNames[0]; *cpp != NULL; ++cpp) {
		strcpy(compoundText, *cpp);
		EnterNewSymbol();
		xEnterNewSymbolValue();
		IncrementCurrentClassValue();
	}
	for (; cpp < &errorCodeNames[(sizeof(errorCodeNames)/sizeof(char *))]; ++cpp)
		*cpp = NULL;  /* Blank rest of these also */

	symCurrentClass = cNotFound;

	/* Initialize Next Values */
	symNextValue[(int)cNotFound] = nullValue;
	for (i = (SymbolClass)((int)cNotFound + 1); i != maxClasses;
	     i = (SymbolClass)((int)i + 1))
		symNextValue[(int)i] = 0;

	symNextValue[(int)cError] = (int)firstUserErrorSignalValue;
	symNextValue[(int)cUpdateOp] = (int)firstUserSemanticOperationValue;
	symNextValue[(int)cParmUpdateOp] = (int)firstUserSemanticOperationValue;
	symNextValue[(int)cChoiceOp] = (int)firstUserSemanticOperationValue;
	symNextValue[(int)cParmChoiceOp] = (int)firstUserSemanticOperationValue;
	symNextValue[(int)cType] = (int)firstTypeClass;
}


/* The Call Table Mechanism */

/*
 * The Call Table is used to keep track of calls to rules
 * and provides operations to resolve the call addresses. 
 */

void
EnterCall()
{
	SymbolIndex s;

	if (callTop < maxCalls) {
		++callTop;
		callAddress[callTop] = outputPointer - 1;
		CurrentSymbolIndex(s);
		callRule[callTop] = s;
	} else
		Error(eTooManyCalls);
}


void
ResolveCalls()
{
	u_int i;
	int v;
	const char *n;

	i = 0;
	while (i != callTop) {
		++i;
		SetCurrentSymbol(callRule[i]);
		SymbolValue(v);
		SymbolName(n);

		if (v != nullValue)
			EmitFixup(callAddress[i], v, n);
		else {
			CopySymbolTextToTokenBuffer();
			Error(eUnresolvedRule);
		}
	}
}


void
InitCallTable()
{
	callTop = 0;
}


/*
 * The Cycle Stack Mechanism 
 *
 * The Cycle Stack is used to handle the exits and loop
 * jump of the cycle construct.				
 */


/* This procedure processes the beginning of a cycle */

void
PushCycle()
{
	if (cycleTop < maxCycles) {
		++cycleTop;
		cycleExitIndex[cycleTop] = exitTop;
		cycleAddress[cycleTop] = outputPointer;
	} else
		Error(eCyclesTooDeep);
}


/* This procedure processes cycle exits */

void
EnterCycleExit()
{
	if (exitTop < maxExits)
		exitAddress[++exitTop] = outputPointer - 1;
	else
		Error(eTooManyExits);
}


void
ResolveCycleExits()
{
	u_int i;

	if (cycleTop == 0)
		assertionFailure;

	/* Fixup cycle exits */
	for (i = exitTop; i != cycleExitIndex[cycleTop]; --i)
		EmitJumpFixup(exitAddress[i]);
}


void
PopCycle()
{
	if (cycleTop == 0)
		assertionFailure;

	exitTop = cycleExitIndex[cycleTop--];
}


void
EmitCycleAddress()
{
	EmitJumpAddress(cycleAddress[cycleTop]);
}


void
InitCycleStack()
{
	exitTop = 0;
	cycleTop = 0;
}


/*
 * The Choice Stack Mechanism 
 *
 * The Choice Stack is used to handle the labels, merge branches,
 * and choice table of the choice construct.				
 */

/* This procedure processes the beginning of a choice */

void
PushChoice(pushClass)
	SymbolClass pushClass;
{
	if (choiceTop < maxChoices) {
		++choiceTop;
		choiceClass[choiceTop] = pushClass;
		choiceAddress[choiceTop] = outputPointer - 1;
		choiceMergeIndex[choiceTop] = mergeTop;
		choiceLabelIndex[choiceTop] = labelTop;
	} else
		Error(eChoicesTooDeep);
}


void
EnterChoiceMerge()
{
	if (choiceTop == 0)
		assertionFailure;

	if (mergeTop < maxMerges)
		mergeAddress[++mergeTop] = outputPointer - 1;
	else
		Error(eTooManyMerges);
}


/* Resolve the merge jumps for the current top choice */

void
ResolveChoiceMerges()
{
	u_int i;

	if (choiceTop == 0 || mergeTop == 0)
		assertionFailure;

	/* Fix choice merges */
	for (i = mergeTop; i != choiceMergeIndex[choiceTop]; --i)
		EmitJumpFixup(mergeAddress[i]);
}


void
xEnterChoiceLabel(value)
	int value;
{
	if (choiceTop == 0)
		assertionFailure;

	if (labelTop < maxLabels) {
		++labelTop;
		labelValue[labelTop] = value;
		labelAddress[labelTop] = outputPointer;
	} else
		Error(eTooManyLabels);
}


Validity
VerifyChoiceLabel(value)
	int value;
{
	u_int i;

	if (choiceTop == 0)
		assertionFailure;

	for (i = choiceLabelIndex[choiceTop]; i != labelTop;) {
		++i;

		if (labelValue[i] == value) {
			/* Duplicate label */
			return invalid;
		}
	}
	return valid;
}


void
PopChoice()
{
	if (choiceTop == 0)
		assertionFailure;
	mergeTop = choiceMergeIndex[choiceTop];
	labelTop = choiceLabelIndex[choiceTop];
	--choiceTop;
}


void
xResolveChoiceTableAddress()
{
	if (choiceTop == 0)
		assertionFailure;

	EmitJumpFixup(choiceAddress[choiceTop]);
}


void
EmitChoiceTable()
{
	u_int i;

	if (choiceTop == 0)
		assertionFailure;


	/* Emit choice table */
	Emit((int)(labelTop - choiceLabelIndex[choiceTop]), NULL); /* Number of entries */
	for (i = choiceLabelIndex[choiceTop]; i != labelTop;) {
		++i;
		Emit(labelValue[i],NULL);
		EmitJumpAddress(labelAddress[i]);
	}
}


void
EmitFirstChoiceAddress()
{
	if (choiceTop == 0)
		assertionFailure;

	EmitJumpAddress(labelAddress[choiceLabelIndex[choiceTop] + 1]);
}


void
xEmitFirstChoiceValue()
{
	if (choiceTop == 0)
		assertionFailure;

	Emit(labelValue[choiceLabelIndex[choiceTop] + 1],NULL);
}


void
ChangeChoiceClass(newClass)
	SymbolClass newClass;
{
	choiceClass[choiceTop] = newClass;
}


void
InitChoiceStack()
{
	mergeTop = 0;
	labelTop = 0;
	choiceTop = 0;
}


/* The Output Definition and S/SL Table Generators */


/* Generates Assembled Constant Definitions for a Class of Symbols */

void
GenerateClass(class, name)
	SymbolClass class;
	const char *name;
{
	SymbolIndex s;
	SymbolClass c;
	int flag, lastvalue;
	char buf[BUFSIZ];

	flag = 1;
	lastvalue = -945876;
	for (s = 0; s != symTop;) {
		++s;
		c = symClass[s];

		if ((c == class
		       || (class == cUpdateOp
				 && (c == cParmUpdateOp
				       || c == cChoiceOp
				       || c == cParmChoiceOp)))
		    && symText[s][0] != quote) {
			 /* A real external symbol and not
			  * a string synonym, so output it  */
			if (flag && name != NULL) {
				fprintf(headerfp, "typedef enum {\n");
				flag = 0;
			}
			if (!isalpha(symText[s][0])) continue;

			if (symValue[s] != lastvalue + 1) {
				sprintf(buf, "%s = %d",
						symText[s], symValue[s]);
			} else
				strcpy(buf, symText[s]);
			fprintf(headerfp, "\t%s,%s/* %d */\n",
				       buf, "\t\t\t\t\t\t"+(strlen(buf)+1)/8,
				       symValue[s]);
			lastvalue = symValue[s];
		}
	}
	if (!flag && name != NULL)
		fprintf(headerfp, "\tlastOf%s\n} %s;\n", name, name);
}


/* Generates Assembled Constant Definitions */

void
GenerateSymbolDefinitions()
{
	SymbolIndex s;
	SymbolClass c;

	fprintf(headerfp, "/* Semantic Operations */\n\n");
	GenerateClass(cUpdateOp, "TableOperation");   /* Does all operations */
	fprintf(headerfp, "\n/* Input Tokens */\n\n");
	GenerateClass(cInput, "InputTokens");
	fprintf(headerfp, "\n/* Output Tokens */\n\n");
	GenerateClass(cOutput, "OutputTokens");
	fprintf(headerfp, "\n/* Input/Output Tokens */\n\n");
	GenerateClass(cInputOutput, "InputOutputTokens");
	fprintf(headerfp, "\n/* Error Codes */\n\n");
	GenerateClass(cError, "ErrorCodes");
	fprintf(headerfp, "\n/* Type Values */\n\n");
	for (s = 0; s != symTop;) {
		++s;
		c = symClass[s];

		if (c == cType) {
			GenerateClass((SymbolClass)symValue[s], symText[s]);
			putc('\n', headerfp);
		}
	}
}


/* Generates the Assembled Output Table */

void
GenerateOutputTable(typeName)
	const char *typeName;
{
	OutputAddress i;
	int col = 16, coladd;

	/* Generate Syntax/Semantic Table */
	fprintf(tablefp, "/* S/SL table (%d entries) */\n",
			    outputPointer);
	fprintf(tablefp, "\n%s sslTable[] = {\n/* %4d */\t", typeName ? typeName:"int", 0);


	for (i = 0; i < outputPointer; ++i) {
if (outputNameTable[i] && outputNameTable[i][0] == '"')
  outputNameTable[i] = 0;
/*outputNameTable[i] = 0;*/
	        if (outputNameTable[i] != NULL)
		  coladd =  (strlen(outputNameTable[i])+2);
		else
		  coladd = 7;
		if (col+coladd > 78) {
			fprintf(tablefp, "\n/* %4d */\t",i);
			col = 16;
		}
		col += coladd;
	        if (outputNameTable[i] != NULL)
		  fprintf(tablefp, "%s, ", outputNameTable[i]);
		else
		  fprintf(tablefp, "%5d, ", outputTable[i]);
	}

	fprintf(tablefp, "0\n};\n");
}

void
GenerateEntryPoints(enumName)
	const char *enumName;
{
	SymbolIndex s;

	if (entryfp == NULL)
		return;
	fprintf(entryfp, "/* Entry points into S/SL Table */\n\n");
	if (enumName)
		fprintf(entryfp, "typedef enum {\n\tnil%s = -1,\n", enumName);
	for (s = 0; s != symTop;) {
		if (symClass[++s] == cRule) {
			if (enumName)
				fprintf(entryfp, "\t%s = %d,\n",
						symText[s], symValue[s]);
			else
				fprintf(entryfp, "#define\tSSL_%s\t%d\n",
						symText[s], symValue[s]);
		}
	}
	if (enumName)
		fprintf(entryfp, "\tlastOf%s\n} %s;\n",
					enumName, enumName);
}

/* Syntax Error Handling */

void
SslGenerateCompoundInputToken(expectedToken)
	InputTokens expectedToken;
{
	if (nextToken != tSyntaxError && nextToken != tEndOfFile)
		assertionFailure;

	compoundToken = expectedToken;
	compoundValue = 0;

	switch (expectedToken) {
	case tInteger:
		strcpy(compoundText, "0");
		break;
	case tString:
		strcpy(compoundText, "'?'");
		break;
	case tIdent:
		strcpy(compoundText, "$NIL");
		break;
	default:
		break;
	}
}


void
SslSyntaxError()
{
	/*
	 * 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 nextToken =
	 * 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 nextToken
	 * not = 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 nextToken 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
	 * nextToken = tSyntaxError), then do nothing
	 * and continue as if the expected token had
	 * been matched.					
	 */

	if (operation != oInput && operation != oInputAny)
		assertionFailure;

	if (nextToken == tSyntaxError) {
		/* Currently recovering from syntax error */
		if (sslTable[sslPointer] == (int)tNewLine
		    || sslTable[sslPointer] == (int)tSemicolon) {
			/* Complete recovery by synchronizing */
			/* input to a new line */ 
			nextToken = savedToken;
			newInputLine = false;
			while (nextToken != tSemicolon
			       && nextToken != tEndOfFile && newInputLine)
				AcceptInputToken();
		}
	} else {
		/* First syntax error on the line */
		if (sslTable[sslPointer] == (int)tNewLine) {
			/* Ignore missing logical newlines */
		} else if (nextToken == tEndOfFile) {
			/* Flag error and terminate processing */
			Error(ePrematureEndOfFile);
			processing = false;
		} else {
			/* Flag error and begin recovery */
			Error(eSyntaxError);
			savedToken = nextToken;
			nextToken = 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((InputTokens)sslTable[sslPointer]);
}


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


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.		
 */

void
SslChoice(choiceTag)
	int choiceTag;
{
	int numberOfChoices;
	u_int choicePointer;

	choicePointer = sslTable[sslPointer];
	numberOfChoices = sslTable[choicePointer];
	++choicePointer;
	choiceTagMatched = false;

	do {
		if (sslTable[choicePointer] == choiceTag) {
			choicePointer = sslTable[choicePointer+1];
			choiceTagMatched = true;
			numberOfChoices = 0;
		} else {
			choicePointer += 2;
			--numberOfChoices;
		}
	} while (numberOfChoices != 0);

	sslPointer = choicePointer;
}


syntax highlighted by Code2HTML, v. 0.9.1