/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 Copyright (c) 1989-2000 Microsoft Corporation

 Module Name:

    ebase.c

 Abstract:

    This file generates the error recovery data base.

 Notes:

    1.  Inputs to this module are the extable.* files generated by yacc in
        response to the s switch.
    2.  Take the state vs token index file ( extable.h3 ), and generate the
        state vs token table using the token index to token value
        translations provided by extable.h1
    3.  Take the state vs expected RHS file ( extable.h2 ) and generate a
        data base of expected RHS in every state.

 Author:

    vibhasc 11-15-91

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

/*****************************************************************************
 local defines and includes
 *****************************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <malloc.h>
#include <string.h>

#include "ebase.h"

#define STATE_VS_TOKEN_INDEX_FILE   "extable.h3"
#define TOKEN_TRANSLATION_FILE      "extable.h1"
#define STATE_VS_EXPECTED_FILE      "extable.h2"
#define ISVALIDTOKEN( i ) (TRUE)
#define MAX_TRANSLATION_LINE_SIZE (512)

#define TRUE 1
#define FALSE 0
                                   /* from winerror.h */
#define ERROR_INVALID_DATA  13

#define CHECK_FSCAN_STATUS( fscanfcall ) \
    if ( EOF == (fscanfcall) ) \
        { \
            fprintf( stderr, \
                     "\nmidleb : error MIDLEB%d : unexpected end of input stream", \
                     ERROR_INVALID_DATA ); \
            exit( ERROR_INVALID_DATA ); \
        }

typedef unsigned int BOOL;

typedef enum _status
    {

    STATUS_OK   = 0,
    OUT_OF_MEMORY,
    CANT_OPEN_INPUT_FILE,
    CANT_OPEN_OUTPUT_FILE,
    WRONG_ARGUMENT_COUNT

    } STATUS_T;

typedef struct _xlat
    {
    char    *   pIncoming;
    char    *   pTranslated;
    struct _xlat *pNext;
    } XLAT;

typedef struct _DBENTRY
    {
    short   State;
    char *  pTranslated;
    } DBENTRY;

/*****************************************************************************
 global data 
 *****************************************************************************/

FILE                    *   hStateVsTokenIndexFile;
FILE                    *   hStateVsExpectedFile;
FILE                    *   hOutput;
FILE                    *   hXlatFile;
FILE                    *   hTokXlatHdl;
SGOTO                   **  pSGoto;
short                   *   pSGotoCount;
short                   **  pTokVsState;
short                   *   pTokVsStateIndex;
short                       ValidStates;
short                       ValidTokens;
char                    *   pPrefix;
XLAT                    *   pXlat = 0, 
                        *   pXlatCur = 0;
DBENTRY                 *   pDataBase;
short                       NTOKENS;
short                       ACCEPTCODE;
short                   *   TokVal;
short                   *   TokCount;
short                       NSTATES;
short                       MAXTOKVSSTATE;
short                       MAXSTATEVSTOK;
short                       MAXTOKENVALUE;
short                       MAXSTATEVSEXPECTED;

/*****************************************************************************
 external procedures
 *****************************************************************************/

STATUS_T                    Init( char *, char * );
STATUS_T                    Generate( FILE * );
STATUS_T                    OpenFileForReadProcessing( FILE **, char * );
void                        Dump( void );
BOOL                        SearchForStateInTokenVsState( short, short );
void                        TranslateExpectedConstructs( void );
char                    *   Translate( char * );


void
main(
    int argc,
    char *argv[] )
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

 Routine Description:
    
    the main routine.

 Arguments:

    Standard

 Return Value:
    
    Exit Status

 Notes:

    Usage : ebase <OutputFilename> <Xlatefile> <prefix> 

    Xlatefile is the file where production names to message translation
    is specified.

    prefix is idl or acf. The expected string array is created with a
    standard name prefixed with the user specified prefix.
----------------------------------------------------------------------------*/
{

    STATUS_T    Status;

    fprintf( stderr, "Error Recovery Data Base Generator\n" );


    if( argc == 4 )
        {

        pPrefix = argv[ 3 ];

        if( (Status = Init( argv[ 1 ], argv[ 2 ] )) == STATUS_OK )
            {
            Status  =   Generate( hStateVsTokenIndexFile );
            }

        Dump();

        TranslateExpectedConstructs();

        }
    else
        {

        fprintf( stderr, "Wrong argument count\n" );
        fprintf( stderr, "Usage : midleb <output file or - > <translation-file-name> <prefix>\n");
        Status = WRONG_ARGUMENT_COUNT;

        }

    exit( Status );

}


/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

 Routine Description:

    Initialize

 Arguments:

    OutputName   : output file name
    XlatFilename : the production/token name to error message translation.

 Return Value:

    STATUS_T    -   OUT_OF_MEMORY or
                -   CANT_OPEN_INPUT_FILE or
                -   STATUS_OK

 Notes:

    Sets up the file handles for all the files that need to be read from

----------------------------------------------------------------------------*/
STATUS_T
Init(
    char * OutputFileName,
    char * XlatFileName )
{
    STATUS_T    Status;
    int         i;

    Status  = OpenFileForReadProcessing(
                                &hStateVsTokenIndexFile,
                                STATE_VS_TOKEN_INDEX_FILE );

    if( Status == STATUS_OK )
        {
        Status  = OpenFileForReadProcessing(
                                    &hStateVsExpectedFile,
                                    STATE_VS_EXPECTED_FILE );

        if( Status == STATUS_OK )
            {
            Status = OpenFileForReadProcessing(
                                    &hTokXlatHdl,
                                    TOKEN_TRANSLATION_FILE );
            if( Status == STATUS_OK )
                {
                Status = OpenFileForReadProcessing( &hXlatFile, XlatFileName );
    
                if( Status == STATUS_OK )
                    {
                    if( strcmp( OutputFileName, "-" ) == 0 )
                        hOutput = stdout;
                    else if( (hOutput = fopen( OutputFileName , "w" )) == (FILE *)0 )
                        {
                        Status = CANT_OPEN_OUTPUT_FILE;
                        };
                    }
                }
            }
        }

    if( Status != STATUS_OK )
        return Status;

    /** read in the required numbers from the TOKEN_TRANSLATION_FILE **/

    CHECK_FSCAN_STATUS( 
        fscanf( hTokXlatHdl, "%hd %hd\n", &NTOKENS, &ACCEPTCODE ) );

    /** read in the token translation table **/

    TokVal = (short *)calloc( 1, NTOKENS * sizeof( short ) );
    TokCount = (short *)calloc( 1, NTOKENS * sizeof( short ) );
        
        if (!TokVal || !TokCount )
            {
            fprintf( stderr, "Out of memory.\n");
            exit(OUT_OF_MEMORY);           
            } 

    for( i = 0;
         i < NTOKENS;
         i++ )
        {
        CHECK_FSCAN_STATUS( 
            fscanf( hTokXlatHdl, "%hd", &TokVal[ i ]) );
        }

    CHECK_FSCAN_STATUS( 
        fscanf( hTokXlatHdl, "\n" ) );

    for( i = 0;
         i < NTOKENS;
         i++ )
        {
        CHECK_FSCAN_STATUS( 
            fscanf( hTokXlatHdl, "%hd", &TokCount[ i ]) );
        }

    CHECK_FSCAN_STATUS( 
        fscanf( hTokXlatHdl, "\n" ) );

    CHECK_FSCAN_STATUS( 
        fscanf( hTokXlatHdl, 
                "%hd %hd %hd %hd %hd\n", 
                &NSTATES, 
                &MAXTOKVSSTATE, 
                &MAXSTATEVSTOK, 
                &MAXTOKENVALUE, 
                &MAXSTATEVSEXPECTED ) ); 

    /** allocate memory now **/

    pSGoto  = (SGOTO **) calloc( 1,NSTATES * sizeof( SGOTO * ) );

    pSGotoCount = (short *)calloc(1, NSTATES * sizeof( short ) );

    pTokVsState = (short **)calloc( 1,(MAXTOKENVALUE+1) * sizeof( short * ) );

    pTokVsStateIndex = (short *)calloc(1, (MAXTOKENVALUE+1) * sizeof( short ) );

    pDataBase = ( DBENTRY * )calloc( 1, MAXSTATEVSEXPECTED * sizeof( DBENTRY ) );


        if( !pSGoto || !pSGotoCount || !pTokVsState || !pTokVsStateIndex || !pDataBase )
            {
            fprintf( stderr, "Out of memory.\n");
            exit(OUT_OF_MEMORY);
            }

    return Status;
}


STATUS_T
Generate(
    FILE    *   hSVsTIndexFile )
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

 Routine Description:
    
    Generate the state vs token table, given the state vs token index table
    in extable.h3 and token vs token index translation of extable.h1.

 Arguments:

    hStateVsTokenIndexFile  -   handle to the file which has the state vs
                                token index info.

 Return Value:

    None.

 Notes:

    The state vs token index file has the goto info for every valid token,
    For every state, this file contains the goto ( or none ) for every valid
    token ( represented by the token index ). Thus for each state, we lookup 
    to see if the state has a goto for a given token index. If it does, then 
    we translate the token index into a token value, and mark the goto for the
    state for that token in the state vs token table. 

    In the end we will have a very sparse array, which contains all the states
    and the gotos for the state for each token ( or the absence of any goto ).

    We will use this table to generate two tables:

        1. The set of all states which have a goto on a token
        2. The set of all tokens valid for any state.

    The exception to this rule is the accept action which is treated like
    an absence of goto.
----------------------------------------------------------------------------*/
    {
    
    short    iState,i,j,Temp,SGotoCount;
    SGOTO   *p;

    /** fixup pointers to token vs state pointer array **/

    for( i = 0;
         i < NTOKENS;
         i++ )
        {

        if( TokCount[ i ] )
            {
            j = TokVal[ i ];

            if( ISVALIDTOKEN( j ) )
                            {
                pTokVsState[ j ] = calloc( 1, TokCount[ i ] * sizeof( short ) );
                            
                            if (!pTokVsState[ j ])
                                {
                                fprintf( stderr, "Out of memory.\n" );
                                exit( OUT_OF_MEMORY );
                                }
                            }
            }

        }

    for( iState = 0;
         iState < NSTATES;
         ++iState )
        {

        /** ignore the state number */

        CHECK_FSCAN_STATUS( 
            fscanf( hSVsTIndexFile,
                    "%hd %c",
                    &Temp,
                    &Temp ) );

        /** get the count of number of state goto entries **/

        CHECK_FSCAN_STATUS( 
            fscanf( hSVsTIndexFile,
                    "%hd %c",
                    &SGotoCount,
                    &Temp ) );

        /** now read in the goto vs token pairs **/


        if( SGotoCount )
            {

            p = pSGoto[ iState ] = calloc( 1, SGotoCount * sizeof( SGOTO ) );

                        if (!p) 
                            {
                            fprintf( stderr, "Out of memory.\n" );
                            exit( OUT_OF_MEMORY );
                            }

            for( j = 0;
                j < SGotoCount;
                ++j )
                {

                CHECK_FSCAN_STATUS( 
                    fscanf( hSVsTIndexFile,
                            "%hd%c %hd",
                            &p->Goto,
                            &Temp,
                            &p->Token ) );

                Temp = TokVal[ p->Token ];

                if( ISVALIDTOKEN( Temp ) )
                    {
                    if( !SearchForStateInTokenVsState( Temp, p->Goto ) )
                        {
                        i = pTokVsStateIndex[ Temp ];
                        pTokVsStateIndex[ Temp ]++;

                        *(pTokVsState[Temp] +  i )  = p->Goto;
                        }
                    p++;
                    pSGotoCount[ iState ]++;

                    }
                }

            }


        }
    return STATUS_OK;
    }

STATUS_T
OpenFileForReadProcessing(
    FILE    **  pHandle,
    char    *   pName )
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

 Routine Description:

    This process opens a file for read processing, reports an error if
    the file could not be opened.

 Arguments:

    pHandle     -   pointer to a file handle deposition area.
    pName       -   pointer to a file name, null terminated.

 Return Value:

    STATUS_T    -   STATUS_OK if all is well.
                -   CANT_OPEN_INPUT_FILE otherwise.
 Notes:

----------------------------------------------------------------------------*/
{
    FILE    *hF;

    if( ( hF = fopen( pName, "r" ) ) == (FILE *)NULL )
        {
        fprintf( stderr, "Cannot open input file : %s\n", pName );
        return CANT_OPEN_INPUT_FILE;
        }
    *pHandle = hF;
    return STATUS_OK;
}


void
Dump( void )
    {
    SGOTO   *   p;
    short       iTemp, i,j;

    /** dump the state goto table **/

    for( iTemp = 0, ValidStates = 0;
         iTemp < NSTATES;
         ++iTemp )
        {

        p = pSGoto[ iTemp ];

        if( j = pSGotoCount[ iTemp ] )
            {
            fprintf( hOutput, "\n SGOTO _sG%s%.4hd [ %d ] = {  ", pPrefix, iTemp, j );

            for( i = 0;
                i < j;
                i++ )
                {

                fprintf(  hOutput
                        , " {%hd, %hd} %c"
                        , p[ i ].Goto
                        , TokVal[ p[ i ].Token]
                        , ( (i+1 == j) ? ' ' : ',' ));
    
                }

            fprintf( hOutput, "};" );

            ValidStates++;
            }
        }


    /** now dump the array of pointers to this **/

    fprintf( hOutput, "\n\n#define VALIDSTATES_%s %d\n", pPrefix, ValidStates );

    fprintf( hOutput, "\n\nSGOTOVECTOR  SGoto%s[ VALIDSTATES_%s ] = {\n",pPrefix, pPrefix);

    for( i = 0;
         i < NSTATES;
         ++i )
        {
        if( pSGotoCount[ i ] )
            {
            fprintf( hOutput, "\n{ %d, _sG%s%.4hd, %d }"
                    , i
                    ,pPrefix
                    , i
                    , pSGotoCount[ i ] );
            fprintf( hOutput,"%c", ((i + 1 == NSTATES) ? ' ' : ',' ));
            }

        }

    fprintf( hOutput, "\n};\n\n" );

    /** count the valid token entries. i.e tokens for which states exist **/

fprintf(hOutput, "#if 0\n");

    for( ValidTokens = 0, i = 0;
         i < MAXTOKENVALUE;
         ++i )
         {

         if( pTokVsStateIndex[ i ] )
            ValidTokens++;
         }

    /** dump the token vs state table **/

    for( iTemp = 0;
         iTemp < NTOKENS;
         ++iTemp )
         {


         if( j = pTokVsStateIndex[ TokVal[ iTemp ] ] )
            {

            fprintf( hOutput, "short _tS%s%.4d[ %d ] = {", pPrefix, TokVal[ iTemp ], j );

            for( i = 0;
                 i < j;
                 ++i )
                {

                fprintf( hOutput, " %d %c", *(pTokVsState[ TokVal[ iTemp ] ]+i),
                         (( i + 1 == j ) ? ' ' : ',' ));

                }

            fprintf( hOutput, "};\n" );

            }
         }

    /** dump the vectors to the token vs state table **/

    fprintf(hOutput, "\n#define VALIDTOKENS %d\n", ValidTokens );
    fprintf( hOutput, "\nTOKVSSTATEVECTOR TokVsState%s[ VALIDTOKENS ] = { \n",pPrefix);

    for( i = 0;
         i < MAXTOKENVALUE+1;
         ++i )
        {

        if( j = pTokVsStateIndex[ i ])
            {
            fprintf( hOutput, "\n{ %d, _tS%s%.4d, %d }",i, pPrefix, i, j );
            fprintf(hOutput, "%c", (i + 1 == NTOKENS) ? ' ' : ',' );
            }


        }

    fprintf( hOutput, "\n\n};\n" );
    fprintf( hOutput, "\n" );

fprintf(hOutput, "#endif\n");

    }


BOOL
SearchForStateInTokenVsState(
    short TokenValue,
    short Goto )
    {
    int i,j;

    for( i = 0, j = pTokVsStateIndex[ TokenValue ];
         i < j;
         ++i )
        {
        if( *(pTokVsState[ TokenValue ] + i) == Goto )
            return TRUE;
        }
    return FALSE;
    }

void
TranslateExpectedConstructs( void )
    {
    int i,State,Count,Temp;
    char Buffer[ MAX_TRANSLATION_LINE_SIZE ];
    char Buffer1[ MAX_TRANSLATION_LINE_SIZE ];
    DBENTRY *p;
    XLAT    *pX;

    /**
        firstly, read in the translation data base, which shows the
        expected token name vs the actual error string the compiler wants to
        output.
     **/

    for(;;)
        {
        i = fscanf( hXlatFile,
                    "%[^ \t]%1s%[^\n]\n",
                    Buffer,
                    &Temp,
                    Buffer1 );

        if( i == EOF || i == 0 )
            break;

        if( ( Buffer[0] != '$' ) && ( Buffer[1] != '$' ) )
            {
            pX = calloc( 1 , sizeof( XLAT ) );

                        if (!pX )
                            {
                            fprintf( stderr, "Out of memory!" );
                            exit(OUT_OF_MEMORY );
                            }

            pX->pIncoming = malloc( strlen( Buffer ) + 1 );
                     
                        if (!pX->pIncoming )
                            {
                            fprintf( stderr, "Out of memory!" );
                            exit(OUT_OF_MEMORY );
                            }
                        
            strcpy( pX->pIncoming, Buffer );

            pX->pTranslated = malloc( strlen( Buffer1 ) + 1 );

                        if (!pX->pTranslated)
                           {
                           fprintf( stderr, "Out of memory!" );
                           exit(OUT_OF_MEMORY );
                           }

            strcpy( pX->pTranslated, Buffer1 );

            if( pXlatCur == 0 )
                {
                pXlatCur = pXlat = pX;
                }
            else
                {
                pXlatCur->pNext = pX;
                pXlatCur = pX;
                }
            }
        }
         

    /**
        Then read the STATE_VS_EXPECTED_FILE, and read in the expected
        tokens/productions for each entry, as translated by looking up the
        data base.
     **/

    p = pDataBase;

    while( p < (pDataBase + MAXSTATEVSEXPECTED) )
        {

        CHECK_FSCAN_STATUS( 
            fscanf( hStateVsExpectedFile, "%d %c %d %c",
                    &State,
                    &Temp,
                    &Count,
                    &Temp,
                    Buffer ) );



        if( Count )
            {
            CHECK_FSCAN_STATUS( 
                fscanf( hStateVsExpectedFile, " %[^\n]\n", Buffer ) );
            p->State = (short) State;
            p->pTranslated = Translate( Buffer );
            p++;
            }
        else
            CHECK_FSCAN_STATUS( 
                fscanf( hStateVsExpectedFile, "\n" ) );

        }
    /**
        emit the state vs expected array with the proper prefix
     **/

    fprintf( hOutput, "\n#ifndef _DBENTRY_DEFINED\n" );
    fprintf( hOutput, "\n#define _DBENTRY_DEFINED\n" );
    fprintf( hOutput, "\ntypedef struct _DBENTRY {" );
    fprintf( hOutput, "\n\t short State;");
    fprintf( hOutput, "\n\t char * pTranslated;");
    fprintf( hOutput, "\n} DBENTRY;\n");
    fprintf( hOutput, "\n#endif\n" );

    fprintf( hOutput, "\n#define MAXSTATEVSEXPECTED_SIZE_%s %d\n", pPrefix, MAXSTATEVSEXPECTED );
    fprintf( hOutput, "\n DBENTRY %s_SyntaxErrorDB[ MAXSTATEVSEXPECTED_SIZE_%s ] = {\n", pPrefix, pPrefix);

    for( p = pDataBase;
         p < (pDataBase + MAXSTATEVSEXPECTED);
         p++ )
        {
        fprintf( hOutput, "{ %d , \"%s\"},\n" , p->State, p->pTranslated );
        }

    fprintf( hOutput, "\n};\n" );

    }

char *
Translate(
    char *pIncoming )
    {
    char *p;

    pXlatCur = pXlat;

    while( pXlatCur )
        {
        if( strcmp( pXlatCur->pIncoming, pIncoming ) == 0 )
            return pXlatCur->pTranslated;
        pXlatCur = pXlatCur->pNext;
        }

    p = malloc( strlen( pIncoming ) + 1 );
 
        if (!p )
            {
            fprintf( stderr, "Out of memory.\n" );
            exit( OUT_OF_MEMORY );
            }

    strcpy( p, pIncoming );

    return p;
    }
    
