NT4/private/ntos/dlc/sm2c/fsmfront.c
2020-09-30 17:12:29 +02:00

513 lines
16 KiB
C

/*++
Copyright (c) 1991 Microsoft Corporation
Copyright (c) 1991 Nokia Data Systems AB
Module Name:
fsmfront.c
Abstract:
The front end of FSM2C (Finite State Machine to C) compiler.
Reads the FSM definition file to the internal data structures.
Author:
Antti Saarenheimo [o-anttis] 07-MAY-1991
Revision History:
21-MAY-1991 (ASa):
Support for multiple state definitions for one transition (as
used in 802.2 state machine specification).
--*/
#include <fsm.h>
typedef enum {
FSM_TOKEN_DEFINE,
FSM_TOKEN_CONSTANTS,
FSM_TOKEN_STATES_DEF,
FSM_TOKEN_STATE_DEF,
FSM_TOKEN_SYNONYMES,
FSM_TOKEN_INPUTS_DEF,
FSM_TOKEN_INPUT,
FSM_TOKEN_VARIABLES,
FSM_TOKEN_STATE,
FSM_TOKEN_NAME,
FSM_TOKEN_TRANSIT,
FSM_TOKEN_CONDITION,
FSM_TOKEN_ACTION,
FSM_TOKEN_NEW_STATE,
FSM_TOKEN_SPEED,
FSM_TOKEN_UNKNOWN_KEY,
FSM_TOKEN_COMMENT,
FSM_TOKEN_INVALID_LINE,
FSM_TOKEN_END_OF_FILE,
FSM_NO_STATE}
FSM_TOKENS;
// internal functions
VOID TokenizeFsmDef(
FILE *fd, // stdio stream handle
PUSHORT pusToken, // Token ID
UCHAR auchToken[], // token string (needed when the key is variable)
PUSHORT pcbToken, // size of the token string
UCHAR auchData[], // data buffer
PUSHORT pcbData, // size of the data buffer
PUSHORT pusLine // current link number
);
INT RemoveChars( PSZ pszStr, PSZ pszRemoved );
VOID ReadDefinitions(
PVOID hHash, PSZ pszInput, USHORT usLine,
PUSHORT pusEnum, PUSHORT pusCount, PBOOL pboolErrorFound,
PFSM_TOKEN FAR * ppDef);
//
// FsmInitTables
//
VOID FsmInitTables( )
{
//
hDefines = StrHashNew( 200 );
hVariables = StrHashNew( 200 );
hSynonymes = StrHashNew( 200 );
hStates = StrHashNew( 200 );
hInputs = StrHashNew( 200 );
hConditions = StrHashNew( 200 );
// allocate the state and input definition tables
ppStateDefs = (PVOID FAR *)Alloc( 512 * sizeof( PVOID ));
ppInputDefs = (PVOID FAR *)Alloc( 1024 * sizeof( PVOID ));
}
//
// This state machine reads the FSM definition file to the
// data structures and does the initial syntax checking for
// the expressions.
//
// Input:
// The file handle of FSM definiton file.
// Output, the global data structures:
//
//
PFSM_TRANSIT FsmFront( FILE *fd, PFSM_TRANSIT pBase )
{
USHORT usMainState = FSM_NO_STATE;
USHORT usSubState;
USHORT usLine = 1, cbToken, cbData;
USHORT usToken;
USHORT usStateEnum = 0, usInputEnum = 0;
PFSM_STR_REDEF pDef;
PFSM_TOKEN pStr;
PFSM_TRANSIT pTransit = NULL;
PVOID hCurrent;
PSZ pszCurState = NULL;
BOOL boolErrorFound = FALSE;
static PSZ pszToken = 0, pszData = 0;
if (pszToken == 0)
pszToken = Alloc( MAX_LINE_LEN );
if (pszData == 0)
pszData = Alloc( MAX_LINE_LEN );
// read the definitons
for (;;)
{
TokenizeFsmDef(
fd, &usToken, pszToken, &cbToken, pszData, &cbData, &usLine);
// handle the main state transitions (they can be anywhere!)
if (usToken == FSM_TOKEN_STATE)
{
usSubState = usMainState = usToken;
break;
}
else if (usToken == FSM_TOKEN_DEFINE)
{
usMainState = usToken;
}
else if (usToken == FSM_TOKEN_END_OF_FILE)
{
// end of this loop
break;
}
else if (usMainState == FSM_TOKEN_DEFINE)
{
if (usToken == FSM_TOKEN_CONSTANTS ||
usToken == FSM_TOKEN_SYNONYMES ||
usToken == FSM_TOKEN_VARIABLES ||
usToken == FSM_TOKEN_STATES_DEF)
{
usSubState = usToken;
}
else if (usToken == FSM_TOKEN_NAME)
{
pszFsmName = StrAlloc( pszData );
}
else
{
// reding the definitions
switch (usSubState)
{
case FSM_TOKEN_CONSTANTS:
hCurrent = hDefines;
break;
case FSM_TOKEN_SYNONYMES:
hCurrent = hSynonymes;
break;
case FSM_TOKEN_STATES_DEF:
if (usToken == FSM_TOKEN_INPUT)
{
ReadDefinitions(
hInputs, pszData, usLine,
&usInputEnum, &cInputs, &boolErrorFound,
ppInputDefs );
}
else if (usToken == FSM_TOKEN_STATE_DEF)
{
ReadDefinitions(
hStates, pszData, usLine,
&usStateEnum, &cStates, &boolErrorFound,
ppStateDefs );
}
else
{
PrintErrMsg(
usLine, FSM_ERROR_INVALID_LINE, pszToken );
boolErrorFound = TRUE;
}
break;
case FSM_TOKEN_VARIABLES:
hCurrent = hVariables;
break;
}
if (usSubState != FSM_TOKEN_STATES_DEF)
{
if (usToken != FSM_TOKEN_UNKNOWN_KEY || cbData == 0)
{
PrintErrMsg( usLine, FSM_ERROR_INVALID_LINE, pszToken );
}
else
{
// read the definition
if (HashSearch( hCurrent, &pszToken ) != NULL)
{
PrintErrMsg(
usLine, FSM_ERROR_ALREADY_EXIST, pszToken );
}
else
{
NEW( pDef );
pDef->pszOrginal = StrAlloc( pszToken );
pDef->pszReplace = StrAlloc( pszData );
HashAdd( hCurrent, pDef);
}
}
}
}
}
else if (usMainState == FSM_NO_STATE ||
usToken == FSM_TOKEN_INVALID_LINE)
{
// we start from here
boolErrorFound = TRUE;
PrintErrMsg( usLine, FSM_ERROR_INVALID_LINE, pszToken );
}
}
// read the actions and state transitions
for (;;)
{
TokenizeFsmDef(
fd, &usToken, pszToken, &cbToken, pszData, &cbData, &usLine);
if (usToken == FSM_TOKEN_END_OF_FILE)
{
// end of this loop
break;
}
else if (usToken == FSM_TOKEN_STATE)
{
usSubState = usToken;
pszCurState = NULL;
}
else if (usToken == FSM_TOKEN_TRANSIT)
{
if (pszCurState == NULL)
{
boolErrorFound = TRUE;
PrintErrMsg( usLine, FSM_ERROR_STATE_NOT_DEFINED, pszToken );
}
usSubState = usToken;
// check the previous state transition, the input and
// action fields must be non empty
if (pTransit != NULL &&
(pTransit->pszAction == NULL || pTransit->pszInput == NULL ||
!*pTransit->pszAction || !*pTransit->pszInput))
{
boolErrorFound = TRUE;
PrintErrMsg(
pTransit->usLine, FSM_ERROR_MISSING_FIELD, NULL );
}
// add the transition to a linked list
NEW( pTransit );
pBase = LinkElement( pBase, pTransit );
pTransit->usLine = usLine;
pTransit->usNewState = -1;
pTransit->pszCurState = pszCurState;
}
else if (usSubState == FSM_TOKEN_STATE)
{
if (usToken == FSM_TOKEN_NAME)
{
pszCurState = StrAlloc( pszData );
}
else
{
// error !!!
boolErrorFound = TRUE;
PrintErrMsg( usLine, FSM_ERROR_INVALID_LINE, pszToken );
}
}
else if (usSubState == FSM_TOKEN_TRANSIT && cbData > 0)
{
switch (usToken)
{
case FSM_TOKEN_INPUT:
pTransit->pszInput = StrAlloc( pszData );
break;
case FSM_TOKEN_CONDITION:
pTransit->pszCondition = StrAlloc( pszData );
break;
case FSM_TOKEN_ACTION:
pTransit->pszAction = StrAlloc( pszData );
break;
case FSM_TOKEN_NEW_STATE:
if (cbData != 0)
{
if ((pStr = HashSearch( hStates, &pszData )) != NULL)
pTransit->usNewState = pStr->usToken;
else
{
boolErrorFound = TRUE;
PrintErrMsg(
usLine, FSM_ERROR_INVALID_LINE, pszData );
}
}
break;
case FSM_TOKEN_SPEED:
if (cbData != 0)
{
pTransit->usSpeed = atoi( pszData );
}
break;
default:
// error !!!
boolErrorFound = TRUE;
PrintErrMsg( usLine, FSM_ERROR_INVALID_LINE, pszToken );
break;
}
}
}
if (boolErrorFound)
return NULL;
else
return pBase;
}
//
// Tokens bound to strings
//
FSM_TOKEN aFsmKeys[] = {
{"[[Define]]", FSM_TOKEN_DEFINE},
{"[Constants]", FSM_TOKEN_CONSTANTS},
{"[StateInputs]", FSM_TOKEN_STATES_DEF},
{"State", FSM_TOKEN_STATE_DEF},
// {"[Inputs]", FSM_TOKEN_INPUTS_DEF},
{"Input", FSM_TOKEN_INPUT},
{"[Variables]", FSM_TOKEN_VARIABLES},
{"[Synonymes]", FSM_TOKEN_SYNONYMES},
{"[[State]]", FSM_TOKEN_STATE},
{"Name", FSM_TOKEN_NAME},
{"[Transition]", FSM_TOKEN_TRANSIT},
{"Predicate", FSM_TOKEN_CONDITION},
{"Action", FSM_TOKEN_ACTION},
{"NewState", FSM_TOKEN_NEW_STATE},
{"Speed", FSM_TOKEN_SPEED},
{ NULL, FSM_TOKEN_UNKNOWN_KEY}};
#define MAX_FSM_C_LINE MAX_LINE_LEN * 20
//
// Procedure reads the next (semantic) line from the FSM definition file.
// The lines having '\' in the last character will be catenated to the
// next on (just as in C)
//
VOID TokenizeFsmDef(
FILE *fd, // stdio stream handle
PUSHORT pusToken, // Token ID
PSZ pszToken, // token string (needed when the key is variable)
PUSHORT pcbToken, // size of the token string
PSZ pszData, // data buffer
PUSHORT pcbData, // size of the data buffer
PUSHORT pusLine // current link number
)
{
static UCHAR auchLine[ MAX_FSM_C_LINE ];
USHORT i, cbData;
for (;;)
{
if (fgets(auchLine, MAX_LINE_LEN - 1, fd ) == NULL)
{
*pusToken = FSM_TOKEN_END_OF_FILE;
break;
}
(*pusLine)++;
// remove all tabulators, spaces, newlines, carrige returns from
// the read line (makes the handling much simpler
RemoveChars( auchLine, " \t\n\r");
ReadExpression(
auchLine, pszToken, pcbToken, pszData, pcbData, "=", "");
// skip all empty files and comments
if (!*pszToken || *pszToken == ';')
continue;
// search the token
for (i = 0; aFsmKeys[i].pszToken != NULL; i++)
if (!_stricmp( aFsmKeys[i].pszToken, pszToken ))
break;
*pusToken = aFsmKeys[i].usToken;
// catenate all lines together having a backslash in the end
cbData = *pcbData;
while (pszData[--cbData] == '\\' &&
cbData < MAX_FSM_C_LINE - MAX_LINE_LEN)
{
if (fgets(pszData + cbData, MAX_LINE_LEN - 1, fd) != NULL)
{
(*pusLine)++;
RemoveChars( pszData + cbData, " \t\n\r");
*pcbData = cbData = strlen( pszData );
}
else
{
*pusToken = FSM_TOKEN_END_OF_FILE;
break;
}
}
break;
}
}
//
// Procedure removes all characters in the second string from the first one.
//
INT RemoveChars( PSZ pszStr, PSZ pszRemoved )
{
USHORT cbRemoved = strlen( pszRemoved );
USHORT iSrc, iDest;
for (iSrc = iDest = 0; pszStr[iSrc]; iSrc++, iDest++)
{
while (memchr(pszRemoved, pszStr[iSrc], cbRemoved)) iSrc++;
pszStr[iDest] = pszStr[iSrc];
}
return iDest - 1;
}
//
// Reads two strings separated by a set of separators and terminated
// by terminators. Returs the length of the read bytes. Skips over all
// terminators in the end of the string.
//
INT ReadExpression(
PSZ pszSrc, // source string
PSZ pszDest1, // the first word
PUSHORT pusDest1, // its length
PSZ pszDest2, // the second word
PUSHORT pusDest2, // its length
PSZ pszSepars, // the separators
PSZ pszTermins // the terminators
)
{
USHORT i;
BOOL boolSeparsFound = FALSE;
USHORT cbSepars = strlen( pszSepars );
USHORT iDest1 = 0, iDest2 = 0;
USHORT cbTermins = strlen( pszTermins );
for (i = 0; pszSrc[i]; i++)
{
if (!boolSeparsFound && memchr(pszSepars, pszSrc[i], cbSepars))
{
boolSeparsFound = TRUE;
}
else
{
if (memchr(pszTermins, pszSrc[i], cbTermins))
break;
if (boolSeparsFound)
pszDest2[iDest2++] = pszSrc[i];
else
pszDest1[iDest1++] = pszSrc[i];
}
}
pszDest1[iDest1] = 0;
if (boolSeparsFound)
pszDest2[iDest2] = 0;
// skip over all termination characters in the end of read data
while (pszSrc[i] && memchr(pszTermins, pszSrc[++i], cbTermins) != NULL);
*pusDest1 = iDest1;
*pusDest2 = iDest2;
return i;
}
//
// Procedure reads an input and state defintion lile.
// The format of the line is similar to C- enumeration:
// The states and inputs can be set values
//
VOID ReadDefinitions(
PVOID hHash, PSZ pszInput, USHORT usLine,
PUSHORT pusEnum, PUSHORT pusCount, PBOOL pboolErrorFound,
PFSM_TOKEN FAR * ppDefTbl)
{
UCHAR auchBuf1[MAX_LINE_LEN]; // max input string length
UCHAR auchBuf2[MAX_LINE_LEN]; // max input string length
PSZ pszToken = (PSZ)auchBuf1;
PSZ pszData = (PSZ)auchBuf2;
USHORT cbToken, cbData;
PFSM_TOKEN pStr;
do
{
pszInput +=
ReadExpression(
pszInput, pszToken, &cbToken, pszData, &cbData, "=", ",");
// read the definition
if (HashSearch( hHash, &pszToken ) != NULL)
{
PrintErrMsg(
usLine, FSM_ERROR_ALREADY_EXIST, pszData );
*pboolErrorFound = TRUE;
}
else
{
NEW( pStr );
pStr->pszToken = StrAlloc( pszToken );
if (cbData != 0 && isdigit( *pszData ))
(*pusEnum) = atoi( pszData );
pStr->usEnum = (*pusEnum)++;
ppDefTbl[(*pusCount)] = pStr;
pStr->usToken = (*pusCount)++;
HashAdd( hHash, pStr );
}
} while( *pszInput );
}