/*****************************************************************************/
/**						Microsoft LAN Manager								**/
/**				Copyright(c) Microsoft Corp., 1987-1999						**/
/*****************************************************************************/
/*****************************************************************************
File				: erep.cxx
Title				: error reporting and recovery utility routines
Description			: contains associated routines for the grammar (pass 1)
History				:
	02-Jan-1992	VibhasC	Created
*****************************************************************************/

#pragma warning ( disable : 4514 )

/****************************************************************************
 *			include files
 ***************************************************************************/

#include "nulldefs.h"
extern	"C" {
	#include <stdio.h>
	#include <ctype.h>
	#include <string.h>
	
	#include "ebase.h"
	#include "idlerec.h"
	#include "acferec.h"
	#include "grammar.h"
	}
#include <limits.h>
#include "common.hxx"
#include "errors.hxx"
#include "filehndl.hxx"
#include "control.hxx"


/****************************************************************************
 *			external data
 ***************************************************************************/

extern CCONTROL	*	pCompiler;

/****************************************************************************
 *			external functions
 ***************************************************************************/

/****************************************************************************
 *			local  functions
 ***************************************************************************/

/****************************************************************************
 *			local definitions 
 ***************************************************************************/

#define IDENTIFIER_WEIGHT	(6)
#define NUMBER_WEIGHT		(6)
#define LBRACE_WEIGHT		(5)
#define RBRACE_WEIGHT		(5)
#define RBRACK_WEIGHT		(4)
#define LBRACK_WEIGHT		(4)
#define SEMI_WEIGHT			(3)
#define LPARAN_WEIGHT		(2)
#define RPARAN_WEIGHT		(2)
#define COMMA_WEIGHT		(1)
#define NO_WEIGHT			(0)

short					TokenWeight( short );

typedef short ELEMTYPE;

#define COMPARE_LESS( a, b )	 	( TokenWeight(a) < TokenWeight(b) )
#define COMPARE_GREATER( a, b ) 	( TokenWeight(a) > TokenWeight(b) )

extern char			*	GetExpectedSyntax( char *, short );
void					qsort( ELEMTYPE a[], int l, int r );
int						SearchForGotoStates( short, SGOTO ** );
BOOL					IsValidTokenInState( SGOTO *, short );

/*
   search in the state vs token table to see if a possible missing token
   can be detected. This routine is called when there is a syntax error, and
   a missing token may be the case. A token is a missing token, if the current
   state has a goto where the current token is valid. 

   For each state in the goto entries for the current state, check if the
   current token is a valid token. If it is , then this is a possible missing
   token.

   After the list of possinble missing tokens has been made, the tough part
   is deciding which is the best possible token. Im afraid, the only simple
   thing right now is to decide on the token, on some kind of priority basis.
   Later we might extend the parser semantic actions to indicate which token
   is the token of choice. But for now, this stays.
 */


short
PossibleMissingToken(
	short	State,
	short	CurrentToken)
	{
	short		Token;
	short	*	pToken;
	short	*	pTokenSave;
	SGOTO	*	pSGoto;
	int			Count, i;


	/*
	 * search for the states goto array
	 */

	Count = SearchForGotoStates( State, &pSGoto );

	if( !Count ) return -1;

	pToken = pTokenSave = new short[ Count ];

	/*
	   for each goto in the array, search for the state where the current
	   token is valid.
	 */
	
	for( i = 0;
		 i < Count;
		 i++ )
		 {

		 if( IsValidTokenInState( pSGoto + i, CurrentToken ))
			{
			Token 	  = (pSGoto+i)->Token;

		 	if( (Token < 128 )			||
			 	(Token == IDENTIFIER)	||
			 	(Token == NUMERICCONSTANT))
				{
				*pToken++ = Token;
				}

			}

		 }
	/*
		if we cannot make any intelligent decision about the lookahead token
		pick up the ones in the current lookahead set

	 */

	if( (pToken - pTokenSave) == 0 )
		{
		delete pTokenSave;
		return -1;
		}

	if( pCompiler->GetPassNumber() == IDL_PASS )
		{
		if( (pToken - pTokenSave) == 0 )
			{
			for( i = 0;
		 		i < Count;
		 		i++ )
		 		{
		
	
				Token = (pSGoto+i)->Token;
	
		 		if( (Token < 128 )			||
			 		(Token == IDENTIFIER)	||
			 		(Token == NUMERICCONSTANT))
					{
					*pToken++ = Token;
					}
	
		 		}
			}
		}


	/*
		We now have a list of possible tokens. Sort them in the order of
		priority.
	 */
	
	if( pToken - pTokenSave )
		{
        MIDL_ASSERT( (pToken - pTokenSave - 1) <= INT_MAX );
		qsort( pTokenSave, 0, (int) (pToken - pTokenSave - 1));

		/* return the first in the list */

		CurrentToken =  *pTokenSave;

		}
	else
		CurrentToken = -1;


	delete pTokenSave;

	return CurrentToken;
	}

int
SearchForGotoStates(
	short	State,
	SGOTO	**	ppSGoto)
	{
	int				i;
	SGOTOVECTOR	*	p = SGotoIDL;
	short			ValidStates;

	if( (pCompiler->GetPassNumber() == IDL_PASS ) )
		{
		ValidStates = VALIDSTATES_IDL;
		p = SGotoIDL;
		}
	else
		{
		ValidStates = VALIDSTATES_ACF;
		p = SGotoACF;
		}

	for( i = 0; i < ValidStates; ++i,++p )
		{
		if( p->State == State )
			{
			*ppSGoto = p->pSGoto;
			return p->Count;
			}
		}
	return 0;
	}

BOOL
IsValidTokenInState(
	SGOTO *	pSGoto,
	short	Token )
	{
	int			Count,i;
	SGOTO	*	p;
	short		State = pSGoto->Goto;
	
	Count = SearchForGotoStates( State, &p );

	if( !Count )
		{

	if( pCompiler->GetPassNumber() == IDL_PASS )
		{
		if( ( (pSGoto->Token == IDENTIFIER ) ||
			  (pSGoto->Token == NUMERICCONSTANT) &&
			  (Token == (short) ';' )) )
			  {
			  return TRUE;
			  }
		}

		return FALSE;
		}

	for( i = 0; i < Count; ++i, ++p )
		{
		if( p->Token == Token )
			return TRUE;
		}
	return FALSE;
	}

void
qsort( 
	ELEMTYPE	a[],
	int			l,
	int			r )
	{

	ELEMTYPE	v, t;
	int			i, j;


	if( r > l )
		{

		v	= a[ r ];
		i 	= l - 1;
		j	= r;

		for (;; )
			{

			/** sort in reverse order of token weight **/

			while( COMPARE_GREATER( a[ ++i ], v ) );
			while( COMPARE_LESS( a[ --j ], v ) );

			if( i >= j ) break;

			t		= a[ i ];
			a[ i ]	= a[ j ];
			a[ j ] 	= t;

			}

		t		= a[ i ];
		a[ i ]	= a[ r ];
		a[ r ] 	= t;

		qsort( a, l, i - 1 );
		qsort( a, i+1, r );
		}
	}

char *
GetExpectedSyntax(
	char	*	pBuffer,
	short		state )
	{
	int 		i = 0;
	int			Max;
	DBENTRY *	pDB;

	if( (pCompiler->GetPassNumber() == IDL_PASS ) )
		{
		pDB	= IDL_SyntaxErrorDB;
		Max	= MAXSTATEVSEXPECTED_SIZE_IDL;
		}
	else
		{
		pDB	= ACF_SyntaxErrorDB;
		Max	= MAXSTATEVSEXPECTED_SIZE_ACF;
		}

	while( i < Max ) 
		{
		if( pDB[ i ].State == state )
			{
			char fFirst = 1;
			strcpy( pBuffer , "expecting ");

			while( pDB[ i ].State == state )
				{
				// make sure not to report the same translated string twice, when two non-terminals
				// have the same translated string
				if ( !strstr( pBuffer, pDB[ i ].pTranslated ) )
					{
					if( !fFirst )
						strcat( pBuffer, " or ");
					fFirst = 0;
					strcat( pBuffer, pDB[ i ].pTranslated );
					}
				i++;
				}
			return pBuffer;
			}
		else
			i++;
		}
	return (char *)NULL;
	}

short
TokenWeight(
	short	Token)
	{
	switch( Token )
		{
		case IDENTIFIER:			return IDENTIFIER_WEIGHT;

		case NUMERICCONSTANT:		return NUMBER_WEIGHT;

		case (short)(']'):			return RBRACK_WEIGHT;

		case (short)('['):			return LBRACK_WEIGHT;

		case (short)('{'):			return LBRACE_WEIGHT;

		case (short)('}'):			return RBRACE_WEIGHT;

		case (short)(';'):			return SEMI_WEIGHT;

		case (short)('('):			return LPARAN_WEIGHT;

		case (short)(')'):			return RPARAN_WEIGHT;

		case (short)(','):			return COMMA_WEIGHT;

		default:					return NO_WEIGHT;
		}
	}