AutoFSM Example


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.


The input definitions

See the usage page for a description of the attributes supported.
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; };



The resulting header file:

#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 */


And the associated code file:>

#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 */


Viewable With Any Browser


AutoGen, AutoOpts, columns, getdefs, AutoFSM, AutoXDR and these web pages copyright (c) 1999, 2000, 2001 Bruce Korb, all rights reserved.