[= AutoGen5 Template -*- Mode: text -*-
h=%s-fsm.h
c=%s-fsm.c
(setenv "SHELL" "/bin/sh")
(define fmt "")
(shellf "[ -f %1$s-fsm.h ] && mv -f %1$s-fsm.h .fsm.head
[ -f %1$s-fsm.c ] && mv -f %1$s-fsm.c .fsm.code" (base-name))
# AutoGen copyright 1992-2006 Bruce Korb
=]
[=
CASE (suffix) =][=#
PURPOSE:
This template collection will produce a finite state machine based on a
description of such a machine. The presumption is that you generally
do not need to know what the current state is in order to determine what
kind of transition is to be taken. That is to say, there is a global
transition table that is indexed by the current state and the next
transition type to determine the next state and trigger any optional
transition handling code.
The finite state machine may be either the master, driving the other
parts of the program, or act as a subroutine keeping track of state
between calls. Consequently, the "type" attribute may be set to:
looping
If the machine processes transitions until it reaches a terminal
state (error or done).
stepping
If the FSM code will process a single transition and then return.
reentrant
This method is much the same as stepping, except that the caller
must save the current state and provide it on the next call.
In this fashion, an FSM may be used in a multi threaded application.
The machine can be constructed in either of three formats, depending
on the value of the "method" attribute:
callout
This method will use a callout table instead of a switch statement
to implement the machine.
case
This is the alternate implementation method.
none
Do not supply the "method" attribute. Choosing this will cause only
the dispatch table to be created. The implementation code is omitted.
The "type" attribute is then ignored.
YOU SUPPLY:
"state" The list of valid state names. The "init" and "done" states
are automatically added to this. If there are other terminal
states, they must set "nxtSt" to "done".
"event" The list of valid transition types.
"type" The machine structure type: looping, stepping or reentrant
"method" The implementation type: callout table, case statement or none.
"prefix" A prefix to glue onto the front of created names
"cookie" zero, one or more of these each containing a C type and name
suitable for use in a procedure header. It is used to pass
through arguments to implementation code.
"transition" Define the handling for a transition from one state to another.
It contains:
"tst" the starting state(s). This may be one, or a list or '*'
to indicate all states.
"tev" the event that triggers this transition. This may also be
a list of events or a '*'.
"ttype" the transition type. By default it is named after the state
and event names, but by specifying a particular type, multiple
different transitions may invoke the same handling code.
"next" the presumptive destination state. "presumptive" because
the code that handles the transition may select a different
destination. Doing that will violate mathematical models, but
it often makes writing this stuff easier.
THE TEMPLATE PRODUCES:
a header file enumerating the states and events, and declaring the FSM
state machine procedure.
the code file with the implementation (provided "method" was specified).
This source file contains special comments around code that is to be
preserved from one regeneration to the next. BE VERY CAREFUL: if you
change the name of a state or event, any code that was stored under the
old name will not be carried forward and is likely to get lost.
If you change names, then *save your work*.
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
=][=
== h =][=
INCLUDE "fsm-trans.tpl" =][=
INCLUDE "fsm-macro.tpl" =][=
INVOKE preamble
=]
/*
* This file enumerates the states and transition events for a FSM.
*
* te_[=(. pfx)=]_state
* The available states. FSS_INIT is always defined to be zero
* and FSS_INVALID and FSS_DONE are always made the last entries.
*
* te_[=(. pfx)=]_event
* The transition events. These enumerate the event values used
* to select the next state from the current state.
* [=(. PFX)=]_EV_INVALID is always defined at the end.
*/
[=(make-header-guard "autofsm")=]
/*
* Finite State machine States
*
* Count of non-terminal states. The generated states INVALID and DONE
* are terminal, but INIT is not :-).
*/
#define [=(. PFX)=]_STATE_CT [=(+ 1 (count "state"))=]
typedef enum {
[=
(shellf "${CLexe-columns} --spread=1 -I4 -S, -f'%s_ST_%%s' <<_EOF_
INIT
%s
INVALID
DONE
_EOF_" PFX (string-upcase! (join "\n" (stack "state"))) )=]
} te_[=(. pfx)=]_state;
/*
* Finite State machine transition Events.
*
* Count of the valid transition events
*/
#define [=(. PFX)=]_EVENT_CT [=(count "event")=]
typedef enum {
[= compute-transitions =][=
(shellf "${CLexe-columns} --spread=1 -I4 -S, -f'%s_EV_%%s' <<_EOF_
%s
INVALID
_EOF_" PFX (string-upcase! (join "\n" (stack "event"))) )=]
} te_[=(. pfx)=]_event;
[=
CASE method =][=
~* call|case =][=
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
#
# We are implementing the machine. Declare the external =][=
CASE type =][=
~* step|reent =][= make-step-proc mode = "extern " =];[=
=* loop =][= make-loop-proc mode = "extern " =];[=
* =][=
(error (string-append "invalid FSM type: ``" (get "type")
"'' must be ``looping'', ``stepping'' or ``reentrant''" ))
=][=
ESAC =][=
# End external procedure declarations
#
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
#
# We are *NOT* implementing the machine. Define the table =][=
== "" =][=
enumerate-transitions use_ifdef = yes =][=
=* no =][=
enumerate-transitions use_ifdef = yes =][=
* =][=
(error (sprintf
"invalid FSM method: ``%s'' must be ``callout'', ``case'' or ``none''"
(get "method"))) =][=
ESAC =]
#endif /* [=(. header-guard)=] */[=
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
#
# C OUTPUT BEGINS HERE
#
=][=
== c =][=
(if (~ (get "method") "(no.*){0,1}")
(begin (shell "rm -f .fsm.*") (out-delete)) ) =][=
INVOKE preamble
=]
#define DEFINE_FSM
#include "[=(. header-file)=]"
#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.
*/
[=(extract fsm-source "/* %s === USER HEADERS === %s */")=]
#ifndef NULL
# define NULL 0
#endif
[= CASE method =][=
=* "case" =][= enumerate-transitions =][=
=* "call" =][= callback-transitions =][=
ESAC =]
[=IF (=* (get "type") "step")=]
/*
* The FSM machine state
*/
static te_[=(. pfx)=]_state [=(. pfx)=]_state = [=(. PFX)=]_ST_INIT;
[=ENDIF=]
[= emit-invalid-msg =][=
IF (=* (get "method") "call") =][=
`set -- \`sed 's/,//' .fsm.xlist\`` =][=
WHILE `echo $#` =][=
INVOKE build-callback
cb_prefix = (string-append pfx "_do")
cb_name = (shell "echo $1 ; shift") =][=
ENDWHILE echo $# =][=
ENDIF =][=
CASE type =][=
=* loop =][= looping-machine =][=
~* step|reent =][= stepping-machine =][=
ESAC =][=
`rm -f .fsm.*` =][=
ESAC (suffix)
=]
/*
* Local Variables:
* mode: C
* c-file-style: "stroustrup"
* tab-width: 4
* indent-tabs-mode: nil
* End:
* end of [=(out-name)=] */