513 lines
16 KiB
C
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 );
|
|
}
|