Here is a simple example. It decides whether or not the input
properly represents a list of value ranges. The input syntax is
approximately:
[ '!' ] [ <lo-num> ] [ '-' [ <hi-num> ] ] [ ',' \ [ '!' ] [ <lo-num> ] [ '-' [ <hi-num> ] ] ... ]If there is no hyphen, then a number must be present. You should not have more than one "bang" operator, but this FSM does not check for that.
AutoGen Definitions fsm; event = comma, num, dash, bang, eol; state = lonum, dash, hinum; type = looping; method = case; prefix = ex; cookie = "void* cookie"; /* * Define a transition for every valid transition. * Specify the Transition_STate, the TransitionEVent and * what the NEXT state will be. A unique transition * enumeration will be produced for each defined transition. */ transition = { tst = init; tev = num; next = lonum; }; transition = { tst = init; tev = bang; next = init; }; transition = { tst = dash; tev = num; next = hinum; }; /* * Dash transition. Always go to 'dash' state, except when we are in * the 'hinum' or 'dash' state. Then, do the 'invalid' transition. */ transition = { tst = "*"; tev = dash; next = dash; }, { tst = hinum, dash; tev = dash; ttype = invalid; next = invalid; }; /* * Comma transition, other than in 'init'. You cannot have two * commas together and you cannot start with one. */ transition = { tst = "*"; tev = comma; next = init; }, { tst = init; tev = comma; ttype = invalid; next = invalid; }; /* * End of line transition, other than in 'init'. * You cannot end with a comma or without any ranges specified. */ transition = { tst = "*"; tev = eol; next = done; }, { tst = init; tev = eol; ttype = invalid; next = invalid; };
#ifndef EXAMPLE_FSM_H_GUARD #define EXAMPLE_FSM_H_GUARD /* * Finite State machine States * * Count of non-terminal states. EX_ST_INVALID and EX_ST_DONE * are terminal, EX_ST_INIT is not :-). */ #define EX_STATE_CT 4 typedef enum { EX_ST_INIT, EX_ST_LONUM, EX_ST_DASH, EX_ST_HINUM, EX_ST_INVALID, EX_ST_DONE } te_ex_state; /* * Finite State machine transition Events. * * Count of the valid transition events */ #define EX_EVENT_CT 5 typedef enum { EX_EV_COMMA, EX_EV_NUM, EX_EV_DASH, EX_EV_BANG, EX_EV_EOL, EX_EV_INVALID } te_ex_event; /* * Call the FSM. Will return EX_ST_DONE or EX_ST_INVALID */ extern te_ex_state ex_run_fsm( void* cookie ); #endif /* EXAMPLE_FSM_H_GUARD */
#define DEFINE_FSM #include "example-fsm.h" #include <stdio.h> /* * Do not make changes to this file, except between the START/END * comments, or it will be removed the next time it is generated. */ /* START === USER HEADERS === DO NOT CHANGE THIS COMMENT */ /* END === USER HEADERS === DO NOT CHANGE THIS COMMENT */ #ifndef NULL # define NULL 0 #endif #ifndef tSCC # define tSCC static const char #endif /* * Enumeration of the valid transition types * Some transition types may be common to several transitions. */ typedef enum { EX_TR_DASH_COMMA, EX_TR_DASH_EOL, EX_TR_DASH_NUM, EX_TR_HINUM_COMMA, EX_TR_HINUM_EOL, EX_TR_INIT_BANG, EX_TR_INIT_DASH, EX_TR_INIT_NUM, EX_TR_INVALID, EX_TR_LONUM_COMMA, EX_TR_LONUM_DASH, EX_TR_LONUM_EOL } te_ex_trans; #define EX_TRANSITION_CT 12 /* * the state transition handling map * This table maps the state enumeration + the event enumeration to * the new state and the transition enumeration code (in that order). * It is indexed by first the current state and then the event code. */ typedef struct transition t_transition; struct transition { te_ex_state next_state; te_ex_trans transition; }; static const t_transition ex_trans_table[ EX_STATE_CT ][ EX_EVENT_CT ] = { { { EX_ST_INVALID, EX_TR_INVALID }, /* init state */ { EX_ST_LONUM, EX_TR_INIT_NUM }, { EX_ST_DASH, EX_TR_INIT_DASH }, { EX_ST_INIT, EX_TR_INIT_BANG }, { EX_ST_INVALID, EX_TR_INVALID } }, { { EX_ST_INIT, EX_TR_LONUM_COMMA }, /* lonum state */ { EX_ST_INVALID, EX_TR_INVALID }, { EX_ST_DASH, EX_TR_LONUM_DASH }, { EX_ST_INVALID, EX_TR_INVALID }, { EX_ST_DONE, EX_TR_LONUM_EOL } }, { { EX_ST_INIT, EX_TR_DASH_COMMA }, /* dash state */ { EX_ST_HINUM, EX_TR_DASH_NUM }, { EX_ST_INVALID, EX_TR_INVALID }, { EX_ST_INVALID, EX_TR_INVALID }, { EX_ST_DONE, EX_TR_DASH_EOL } }, { { EX_ST_INIT, EX_TR_HINUM_COMMA }, /* hinum state */ { EX_ST_INVALID, EX_TR_INVALID }, { EX_ST_INVALID, EX_TR_INVALID }, { EX_ST_INVALID, EX_TR_INVALID }, { EX_ST_DONE, EX_TR_HINUM_EOL } } }; /* * Define all the event and state names */ tSCC zBogus[] = "** OUT-OF-RANGE **"; tSCC zStInit[] = "init"; tSCC zEvInvalid[] = "* Invalid Event *"; tSCC zFsmErr[] = "FSM Error: in state %d (%s), event %d (%s) is invalid\n"; tSCC zStLonum[] = "lonum"; tSCC zStDash[] = "dash"; tSCC zStHinum[] = "hinum"; tSCC* apzStates[] = { zStInit, zStLonum, zStDash, zStHinum }; tSCC zEvComma[] = "comma"; tSCC zEvNum[] = "num"; tSCC zEvDash[] = "dash"; tSCC zEvBang[] = "bang"; tSCC zEvEol[] = "eol"; tSCC* apzEvents[] = { zEvComma, zEvNum, zEvDash, zEvBang, zEvEol, zEvInvalid }; #define EX_EVT_NAME(t) ( (((unsigned)(t)) >= EX_EV_INVALID) \ ? zBogus : apzEvents[ t ]) #define EX_STATE_NAME(s) ( (((unsigned)(s)) > EX_ST_INVALID) \ ? zBogus : apzStates[ s ]) #ifndef EXIT_FAILURE # define EXIT_FAILURE 1 #endif /* * * * * * * * * THE CODE STARTS HERE * * * * * * * * * * Print out an invalid transition message and return EXIT_FAILURE */ int ex_invalid_transition( te_ex_state st, te_ex_event evt ) { /* START == INVALID TRANS MSG == DO NOT CHANGE THIS COMMENT */ fprintf( stderr, zFsmErr, st, EX_STATE_NAME( st ), evt, EX_EVT_NAME( evt )); /* END == INVALID TRANS MSG == DO NOT CHANGE THIS COMMENT */ return EXIT_FAILURE; } te_ex_state ex_run_fsm( void* cookie ) { te_ex_state ex_state = EX_ST_INIT; te_ex_event trans_evt; te_ex_state nxtSt, firstNext; te_ex_trans trans; while (ex_state < EX_ST_INVALID) { /* START == FIND TRANSITION == DO NOT CHANGE THIS COMMENT */ trans_evt = GET_NEXT_TRANS(); /* END == FIND TRANSITION == DO NOT CHANGE THIS COMMENT */ if (trans_evt >= EX_EV_INVALID) { nxtSt = EX_ST_INVALID; trans = EX_TR_INVALID; } else { const t_transition* pTT = trans_table[ ex_state ] + trans_evt; nxtSt = firstNext = pTT->next_state; trans = pTT->transition; } #ifdef DEBUG printf( "in state %s(%d) step %s(%d) to %s(%d)\n", EX_STATE_NAME( ex_state ), ex_state, EX_EVT_NAME( trans_evt ), trans_evt, EX_STATE_NAME( nxtSt ), nxtSt ); #endif switch (trans) { case EX_TR_DASH_COMMA: /* START == DASH_COMMA == DO NOT CHANGE THIS COMMENT */ nxtSt = HANDLE_DASH_COMMA(); /* END == DASH_COMMA == DO NOT CHANGE THIS COMMENT */ break; case EX_TR_DASH_EOL: /* START == DASH_EOL == DO NOT CHANGE THIS COMMENT */ nxtSt = HANDLE_DASH_EOL(); /* END == DASH_EOL == DO NOT CHANGE THIS COMMENT */ break; case EX_TR_DASH_NUM: /* START == DASH_NUM == DO NOT CHANGE THIS COMMENT */ nxtSt = HANDLE_DASH_NUM(); /* END == DASH_NUM == DO NOT CHANGE THIS COMMENT */ break; case EX_TR_HINUM_COMMA: /* START == HINUM_COMMA == DO NOT CHANGE THIS COMMENT */ nxtSt = HANDLE_HINUM_COMMA(); /* END == HINUM_COMMA == DO NOT CHANGE THIS COMMENT */ break; case EX_TR_HINUM_EOL: /* START == HINUM_EOL == DO NOT CHANGE THIS COMMENT */ nxtSt = HANDLE_HINUM_EOL(); /* END == HINUM_EOL == DO NOT CHANGE THIS COMMENT */ break; case EX_TR_INIT_BANG: /* START == INIT_BANG == DO NOT CHANGE THIS COMMENT */ nxtSt = HANDLE_INIT_BANG(); /* END == INIT_BANG == DO NOT CHANGE THIS COMMENT */ break; case EX_TR_INIT_DASH: /* START == INIT_DASH == DO NOT CHANGE THIS COMMENT */ nxtSt = HANDLE_INIT_DASH(); /* END == INIT_DASH == DO NOT CHANGE THIS COMMENT */ break; case EX_TR_INIT_NUM: /* START == INIT_NUM == DO NOT CHANGE THIS COMMENT */ nxtSt = HANDLE_INIT_NUM(); /* END == INIT_NUM == DO NOT CHANGE THIS COMMENT */ break; case EX_TR_INVALID: /* START == INVALID == DO NOT CHANGE THIS COMMENT */ exit( ex_invalid_transition( ex_state, trans_evt )); /* END == INVALID == DO NOT CHANGE THIS COMMENT */ break; case EX_TR_LONUM_COMMA: /* START == LONUM_COMMA == DO NOT CHANGE THIS COMMENT */ nxtSt = HANDLE_LONUM_COMMA(); /* END == LONUM_COMMA == DO NOT CHANGE THIS COMMENT */ break; case EX_TR_LONUM_DASH: /* START == LONUM_DASH == DO NOT CHANGE THIS COMMENT */ nxtSt = HANDLE_LONUM_DASH(); /* END == LONUM_DASH == DO NOT CHANGE THIS COMMENT */ break; case EX_TR_LONUM_EOL: /* START == LONUM_EOL == DO NOT CHANGE THIS COMMENT */ nxtSt = HANDLE_LONUM_EOL(); /* END == LONUM_EOL == DO NOT CHANGE THIS COMMENT */ break; default: /* START == BROKEN MACHINE == DO NOT CHANGE THIS COMMENT */ exit( ex_invalid_transition( ex_state, trans_evt )); /* END == BROKEN MACHINE == DO NOT CHANGE THIS COMMENT */ } #ifdef DEBUG if (nxtSt != firstNext) printf( "transition code changed destination state to %s(%d)\n", EX_STATE_NAME( nxtSt ), nxtSt ); #endif ex_state = nxtSt; } return ex_state; } /* * Local Variables: * mode: C * c-file-style: "stroustrup" * tab-width: 4 * End: * end of example-fsm.c */