#pragma once
#include <windows.h>
#include <crtdbg.h>
#include "stdio.h"
//-------------------------------------------------------
//-- This class implements base functionality for an fsm.
//-- It is assumed that that an action is performed when a transition occurs
//-- StateId: Each state in the fsm have a unique id associated with it
//-- It is the user's responsibility to ensure that the id's are unique
//--
//-- Input Stimuli: the fsm consumes stimuli provided by the user. The stimulus
//-- is passed into the run() method. eg. input stimuli could be a stream of
//-- characters read from a file or a series of objects
//--
//-- EventId: every input stimuli has an event id associated with it. It is
//-- the user's responsibility to map the correct event id to a given
//-- input stimulus.
//--
//-- One or more input stimuli can map to the same event id, that is an
//-- input stimulus does not have to map uniquely to an event id
//--
//-- A given input stimulus can map to different event id's, that is an
//-- input stimulus does not have to map to the same event id in all states.
//-- In other words, the mapping can be context sensitive depending on the
//-- current state. Each state has a map() function that maps all possible
//-- input stimuli into an event id
//--
//-- Example: a simple three state fsm that toggles back and forth based
//-- on whether the current char is a blank or not. It removes all but
//-- the first blank. Once a 0 char is read, it goes to a done state and
//-- stays there.
//-- The .h looks like this:
//-- #pragma once
//--
//-- #include "fsm.h"
//--
//-- class WhiteFSM : public FSM<char>
//-- {
//-- char m_char; //required by the action routines
//--
//-- protected:
//-- void dumpchar();
//--
//-- public:
//-- WhiteFSM() {}
//-- ~WhiteFSM() {}
//--
//-- void run(char c);
//-- void init();
//-- } ;
//--
//-- The .cpp looks like this
//--
//-- #include "whitefsm.h"
//--
//-- typedef enum _event
//-- {
//-- e_anychar = 1,
//-- e_blankchar,
//-- e_ateof
//-- } EVENTID;
//--
//-- const struct State_base : public State<char>
//-- {
//-- State_base(StateId_t id, bool final) : State<char>(id, final){}
//-- EventId_t map(char c)
//-- {
//-- if (c == 0) return e_ateof;
//-- if (c == ' ') return e_blankchar;
//-- return e_anychar;
//-- }
//-- } ;
//--
//-- const State_base s_start(1, false);
//--const State_base s_blanks(2, false);
//--const State_base s_done(3, true);
//--
//--void WhiteFSM::dumpchar()
//-- {
//-- printf("%c", m_char);
//-- }
//--
//--void WhiteFSM::run(char c)
//-- {
//-- m_char = c;
//-- FSM<char>::run(c);
//-- }
//--
//--void WhiteFSM::init()
//-- {
//-- //curstate event Action nextstate
//-- Add(&s_start, e_ateof, 0, &s_done);
//-- Add(&s_start, e_anychar, (PACTIONFUNC) dumpchar, &s_start);
//-- Add(&s_start, e_blankchar, (PACTIONFUNC) dumpchar, &s_blanks);
//--
//-- Add(&s_blanks, e_blankchar, 0, &s_blanks);
//-- Add(&s_blanks, e_anychar, (PACTIONFUNC) dumpchar, &s_start);
//-- Add(&s_blanks, e_ateof, 0, &s_done);
//--
//-- Add(&s_done, e_anychar, 0, &s_done);
//--
//-- SetCurrentState(&s_start);
//-- }
//--
//--
//--
//--
//--
//--
//--
//--
//--
//--
//--
//--
//--
//--
//--
//--
//--
//--
//--
//--
//-------------------------------------------------------
//-------------------------------------------------------
//every state and event class has a unique id associated with it
typedef long StateId_t; //the enumuration that every state maps to
typedef long EventId_t; //the enumeration that every event maps to
//------------------------------------------------------
//the base state class
template <class STIMULUS>
class State
{
public:
// id is a unique identifier for each state class
// final indicates if this state is a final state or not
State(StateId_t id, bool final) : m_id(id), m_bFinal(final) {}
virtual ~State() {}
//returns true if the state is a final state
virtual bool isFinal() {return m_bFinal;}
//returns the unique id for the state class
virtual StateId_t map() {return m_id;}
//for a given input stimulus, returns an id that is used by the fsm to cause a transition
//each state can map similar input stimuli differently if they want
virtual EventId_t map(STIMULUS) = 0;
protected:
StateId_t m_id;
bool m_bFinal;
private:
State() {} // no default ctor
State(const State& ) {} //no copy ctor
} ;
//------------------------------------------------------
//the base fsm class
template <class STIMULUS>
class FSM
{
public:
//return true if the fsm is in a final state
bool isFinal() {return m_sCurrentState->isFinal();}
protected:
//this is the prototype for every action function in the fsm table
typedef void (FSM<STIMULUS>::*PACTIONFUNC) ();
typedef State<STIMULUS> State_t;
//you must derive this FSM class to use it
FSM() : m_iMaxStateTableIndex(0),
m_sCurrentState(0),
m_lCurrentStateId(-1)
{}
virtual ~FSM() {}
//Sets the current state to the given parameter.
void SetCurrentState(const State_t* s)
{
m_sCurrentState = const_cast<State_t*>(s);
m_lCurrentStateId = m_sCurrentState->map();
}
//adds a state to the fsm
//s1 is the current state
//ev is the event id to transition on
//s2 is the next state to go to if the fsm is in s1 and ev comes in
//f is the action to take when the transition occurs
void Add(const State_t* s1, EventId_t ev, PACTIONFUNC f, const State_t* s2)
{
_ASSERTE(s1);
_ASSERTE(s2);
_ASSERTE(ev);
m_table[m_iMaxStateTableIndex].curstate = const_cast<State_t*>(s1);
m_table[m_iMaxStateTableIndex].curstateid = m_table[m_iMaxStateTableIndex].curstate->map();
m_table[m_iMaxStateTableIndex].eventid = ev;
m_table[m_iMaxStateTableIndex].fn = f;
m_table[m_iMaxStateTableIndex].nextstate = const_cast<State_t*>(s2);
m_table[m_iMaxStateTableIndex].nextstateid = m_table[m_iMaxStateTableIndex].nextstate->map();
m_iMaxStateTableIndex++;
}
//run the fsm given the input stimulus
void run(STIMULUS stim)
{
//first map the input stimulus into an event id
EventId_t ev = m_sCurrentState->map(stim);
//search the table for the event id and current state
int i;
for(i = 0; i < m_iMaxStateTableIndex; i++)
{
//if we've found the event id and current state
if (m_table[i].curstateid == m_lCurrentStateId && m_table[i].eventid == ev)
{
//...and there is an action to perform
if (m_table[i].fn)
(this->*m_table[i].fn) (); //do the action
//set the fsm to the next state
m_lCurrentStateId = m_table[i].nextstateid;
m_sCurrentState = m_table[i].nextstate;
_ASSERTE(m_sCurrentState);
_ASSERTE(m_lCurrentStateId == m_sCurrentState->map());
break;
}
}
//assert if the eventid/current state combo is not in the fsm
_ASSERTE(i < m_iMaxStateTableIndex || "unknown transition from current state with event");
}
private:
struct _table
{
State_t* curstate;
StateId_t curstateid;
EventId_t eventid;
PACTIONFUNC fn;
State_t* nextstate;
StateId_t nextstateid;
} m_table[20];
StateId_t m_lCurrentStateId;
State_t* m_sCurrentState;
int m_iMaxStateTableIndex;
} ;
|
#pragma once
#include "myfsm.h"
//these are all of the possible event id's
//all input stimuli map to these id's
typedef enum _event
{
e_any = 1,
e_slash,
e_star,
e_eof,
e_newline
} EVENTID;
//Declare all the states we're going to use
//------------------------------------------------------------
//This is a common class just to keep things simpler for the map() function
class CommonState : public State<char>
{
protected:
//this is used just to ensure that the state id's are unique
typedef enum _stateid
{
e_start = 1,
e_slash1,
e_slash2,
e_star1,
e_star2,
e_error,
e_done
};
public:
CommonState(StateId_t id, bool final) : State<char>(id, final){}
virtual ~CommonState() {}
StateId_t map(char c)
{
if (c == 0) return e_eof;
return e_any;
}
} ;
//------------------------------------------------------------
class StartState : public CommonState
{
public:
StartState() : CommonState(e_start, false) {}
StateId_t map(char c)
{
if (c == '/') return e_slash;
return CommonState::map(c);
}
} const s_StartState;
//------------------------------------------------------------
const class Slash1State : public CommonState
{
public :
Slash1State() : CommonState(e_slash1, false) {}
StateId_t map(char c)
{
if (c == '/') return e_slash;
if (c == '*') return e_star;
return CommonState::map(c);
}
} s_Slash1State;
//------------------------------------------------------------
const class Slash2State : public CommonState
{
public :
Slash2State() : CommonState(e_slash2, false) {}
StateId_t map(char c)
{
if (c == '\n') return e_newline;
return CommonState::map(c);
}
} s_Slash2State;
//------------------------------------------------------------
const class Star1State : public CommonState
{
public :
Star1State() : CommonState(e_star1, false) {}
StateId_t map(char c)
{
if (c == '*') return e_star;
return CommonState::map(c);
}
} s_Star1State;
//------------------------------------------------------------
const class Star2State : public CommonState
{
public :
Star2State() : CommonState(e_star2, false) {}
StateId_t map(char c)
{
if (c == '/') return e_slash;
return CommonState::map(c);
}
} s_Star2State;
//------------------------------------------------------------
const class ErrorState : public CommonState
{
public :
ErrorState() : CommonState(e_error, true) {}
StateId_t map(char c)
{
return e_any;
}
} s_ErrorState;
//------------------------------------------------------------
const class DoneState : public CommonState
{
public :
DoneState() : CommonState(e_done, true) {}
} s_DoneState;
//------------------------------------------------------------
typedef FSM<char> charFSM;
inline void MyFSM::CommentColor()
{
SetConsoleTextAttribute(m_hconsole, FOREGROUND_GREEN);
}
inline void MyFSM::NormalColor()
{
SetConsoleTextAttribute(m_hconsole, FOREGROUND_RED|FOREGROUND_GREEN|FOREGROUND_BLUE);
}
MyFSM::MyFSM()
{
m_hconsole = GetStdHandle(STD_OUTPUT_HANDLE);
}
MyFSM::~MyFSM()
{}
void MyFSM::run(char c)
{
m_char = c;
charFSM::run(c);
}
//Actions
//dump the current char
void MyFSM::dumpchar(void)
{
NormalColor();
printf("%c", m_char);
}
//we're all done;
void MyFSM::alldone()
{
NormalColor();
printf("<--done\n");
}
void MyFSM::dumpslashchar()
{
NormalColor();
printf("/%c", m_char);
}
void MyFSM::dumpslashcomment()
{
CommentColor();
printf("/%c", m_char);
}
void MyFSM::dumpcomment()
{
CommentColor();
printf("%c", m_char);
}
void MyFSM::dumpcommentnewline()
{
CommentColor();
printf("\n");
}
void MyFSM::dumperror()
{
NormalColor();
printf("error: \n");
}
void MyFSM::init()
{
//curstate event Action nextstate
Add(&s_StartState, e_eof, (PACTIONFUNC) &MyFSM::alldone, &s_DoneState);
Add(&s_StartState, e_any, (PACTIONFUNC) &MyFSM::dumpchar, &s_StartState);
Add(&s_StartState, e_slash, 0, &s_Slash1State);
Add(&s_Slash1State, e_any, (PACTIONFUNC) &MyFSM::dumpslashchar, &s_StartState);
Add(&s_Slash1State, e_slash, (PACTIONFUNC) &MyFSM::dumpslashcomment, &s_Slash2State);
Add(&s_Slash1State, e_star, (PACTIONFUNC) &MyFSM::dumpslashcomment, &s_Star1State);
Add(&s_Slash2State, e_any, (PACTIONFUNC) &MyFSM::dumpcomment, &s_Slash2State);
Add(&s_Slash2State, e_newline, (PACTIONFUNC) &MyFSM::dumpcommentnewline, &s_StartState);
Add(&s_Slash2State, e_eof, (PACTIONFUNC) &MyFSM::dumperror, &s_ErrorState);
Add(&s_Star1State, e_any, (PACTIONFUNC) &MyFSM::dumpcomment, &s_Star1State);
Add(&s_Star1State, e_star, (PACTIONFUNC) &MyFSM::dumpcomment, &s_Star2State);
Add(&s_Star2State, e_any, (PACTIONFUNC) &MyFSM::dumpcomment, &s_Star1State);
Add(&s_Star2State, e_slash, (PACTIONFUNC) &MyFSM::dumpcomment, &s_StartState);
Add(&s_ErrorState, e_any, 0, &s_DoneState);
SetCurrentState(&s_StartState);
}
|
#pragma once
#include "whitefsm.h"
typedef enum _event
{
e_anychar = 1,
e_blankchar,
e_ateof
} EVENTID;
const struct State_base : public State<char>
{
State_base(StateId_t id, bool final) : State<char>(id, final){}
EventId_t map(char c)
{
if (c == 0) return e_ateof;
if (c == ' ') return e_blankchar;
return e_anychar;
}
} ;
const State_base s_start(1, false);
const State_base s_blanks(2, false);
const State_base s_done(3, true);
void WhiteFSM::dumpchar()
{
printf("%c", m_char);
}
void WhiteFSM::run(char c)
{
m_char = c;
FSM<char>::run(c);
}
void WhiteFSM::init()
{
//curstate event Action nextstate
Add(&s_start, e_ateof, 0, &s_done);
Add(&s_start, e_anychar, (PACTIONFUNC) &WhiteFSM::dumpchar, &s_start);
Add(&s_start, e_blankchar, (PACTIONFUNC) &WhiteFSM::dumpchar, &s_blanks);
Add(&s_blanks, e_blankchar, 0, &s_blanks);
Add(&s_blanks, e_anychar, (PACTIONFUNC) &WhiteFSM::dumpchar, &s_start);
Add(&s_blanks, e_ateof, 0, &s_done);
Add(&s_done, e_anychar, 0, &s_done);
SetCurrentState(&s_start);
}
|