Files
hsmc-cpp/docs/doc.tex
T
2011-01-24 14:18:56 -08:00

953 lines
24 KiB
TeX

\documentclass[titlepage,letterpaper]{report}
\usepackage{makeidx}
\usepackage{verbatim}
\usepackage{listings}
\usepackage[hyperindex,dvipdfm,bookmarksnumbered,colorlinks]{hyperref}
\makeindex
\title{HSM Compiler User Guide}
\author{Stephen Waits}
\begin{document}
% Define State language for listings package
%
\lstdefinelanguage{State}
{morekeywords={Machine,State,Default,Entry,Idle,Exit,Transition,Action,Terminate},
sensitive=false,
morecomment=[l]{//}
}
\lstset{% Configure listings defaults
language=State,
captionpos=b,
tabsize=2,
columns=flexible,
basicstyle=\small,
numberstyle=\tiny,
index={Machine,State,Default,Entry,Idle,Exit,Transition,Action,Terminate}
}
\maketitle
\pagenumbering{roman}
\tableofcontents
\newpage
\lstlistoflistings
\newpage
\pagenumbering{arabic}
\chapter{Introduction}
\section{Overview}
here's an overview of the this document.
\section{Motivation}
why create an HSM language and compiler?
\section{Hierarchical State Machines}
brief description on how hsm's work \cite{harel:sta}.
\chapter{Hierarchical State Machine Compiler}
\section{Overview}
\emph{hsmc} is a command line based compiler, named \emph{hsmc.exe}. It reads an HSM
source input file, compiles it, and outputs two files (\emph{.cpp} and \emph{.h}). Each HSM source
input file must define at least one valid Machine. Only one input file can be compiled
at a time; however, that file may contain multiple Machine definitions (see \ref{hsm:file}).
\section{Running the Compiler}
Invoking \emph{hsmc} from the command line should print a usage statement:
\begin{verbatim}
HSMC - Hierarchical State Machine Compiler
Usage: hsmc [-hdpq] [-o prefix] source_file
-h this help
-d include debugging information
-p actions declared as pure virtual
-q quiet mode (only output errors)
-o prefix specify prefix for output files [prefix.cpp,prefix.h]
source_file HSM source file
\end{verbatim}
To compile your HSM file using all defaults, you need only specify your source
filename. For example:
\begin{verbatim}
hsmc myfile.hsm
\end{verbatim}
outputs two files upon successful compilation: \emph{myfile.cpp} and \emph{myfile.h}.
\subsection{Compiler Options}
\label{hsmc:options}
\begin{description}
\item[-h] Prints the usage statement. This is equivalent to running \emph{hsmc} with no parameters.
\item[-d] Include debugging information in the generated machine. This includes considerably more
calls to HSMDebug. The debug strings are also available via HSMGetLastDebugMessage.
The debug strings consist of descriptions of the current state, current transitions, or
diagnostic messages.
\item[-p] Actions, by default, are declared virtual member methods and defined to be empty. This flag
causes these methods to be declared pure virtual and remain undefined. \label{opt:p}
\item[-q] By default, \emph{hsmc} will output status messages during parsing, compilation, and code
generation. Specifying this flag causes the output to be quieted such that only errors
are emitted.
\item[-o prefix] Specifies the output filename prefix. By default this is set to the input filename
after it's been stripped of its extension. So, for \emph{input.hsm}, the default
output prefix is \emph{input}.
To generate \emph{output.cpp} and \emph{output.h} from the input file \emph{input.hsm},
you should specify the prefix explicitly:
\begin{verbatim}
hsmc -o output input.hsm
\end{verbatim}
The output prefix may include a prepended directory path as well.
\end{description}
\section{Compiler Output}
The \emph{hsmc} compiler outputs a C++ header file and a C++ source file.
\subsection{Header File}
The header file declares one class for each machine defined in the HSM source input
file.
Each machine class enumerates events, enumerates states, declares several methods
common to all machines, and declares all actions (virtual) which should be overriden
before the machine is instanced.
For example, a machine declaration of:
\begin{lstlisting}
Machine(MyMachine,16)
{
...
}
\end{lstlisting}
generates a class declaration of:
\begin{lstlisting}[language=C++]
class MyMachine
{
public:
...
};
\end{lstlisting}
\subsubsection{Event Enumeration}
\label{hsmc:events}
A single \emph{enum} entry is generated for each event identifier referenced in
the HSM source input file. This enumeration is within the scope of the machine
class.
For example, the following HSM source fragment:
\begin{lstlisting}
Action(MOUSE_INPUT,BeepSound);
Transition(KEYBOARD_INPUT,NextState);
Terminate(CRITICAL_EVENT);
\end{lstlisting}
compiles to the following event enumeration:
\begin{lstlisting}[language=C++]
// events
enum
{
MOUSE_INPUT = 0,
KEYBOARD_INPUT,
CRITICAL_EVENT
___HSM_NUM_EVENTS___
};
\end{lstlisting}
\subsubsection{State Enumeration}
\label{hsmc:states}
A single \emph{enum} entry is generated for each state defined for a given machine in
the HSM source input file. This enumeration is within the scope of the machine
class.
For example, the following HSM source fragment:
\begin{lstlisting}
Machine(MyMachine,16)
{
State(BootUp) { ... }
State(AwaitInput) { ... }
State(ProcessInput) { ... }
...
}
\end{lstlisting}
compiles to the following state enumeration:
\begin{lstlisting}[language=C++]
// states
enum
{
TOPSTATE = 0,
BootUp,
AwaitInput,
ProcessInput,
___HSM_NUM_STATES___
};
\end{lstlisting}
The logical name of the root state is \emph{TOPSTATE}, as described in section \ref{hsm:machines}.
\subsubsection{Common Method Declaration}
These method declarations are common to every machine.
\begin{lstlisting}[language=C++]
public:
int HSMGetCurrentState() const;
bool HSMIsRunning() const;
void HSMConstruct();
void HSMDestruct();
bool HSMUpdate(float dt = 0.0f);
void HSMTrigger(int event);
char* HSMGetLastDebugMessage();
protected:
// debug hook (override this to trap debug messages)
virtual void HSMDebug(char* msg);
\end{lstlisting}
\begin{description}
\item[int HSMGetCurrentState() const;]
returns an integer, the enumeration id number of the current state as enumerated
in the event numeration (\ref{hsmc:events}). A negative integer (<0) is returned if
the machine is not running.
\item[bool HSMIsRunning() const;]
returns \emph{true} if the machine has been constructed and is presently in a valid state.
returns \emph{false} if \emph{TOPSTATE} has exited, or the machine has not been constucted,
or the machine has been destroyed.
\item[void HSMConstruct();]
Called on a non-running machine to initialize the HSM, and cause a transition into \emph{TOPSTATE}.
This does nothing if the machine is already running.
\item[void HSMDestruct();]
Called on a running machine to cause an immediate transition to \emph{TOPSTATE}, followed by
the exit of \emph{TOPSTATE}. This does nothing if the machine is not running.
\item[bool HSMUpdate(float dt = 0.0f);]
Updates any timers according to \emph{dt}, adds an \emph{IDLE} event to the end of the event
queue, and then processes all events up to that \emph{IDLE} event.
\item[void HSMTrigger(int event);]
Adds event to the event queue. If event is invalid, it's ignored. The event queue is processed
on the next call to HSMUpdate().
\item[char* HSMGetLastDebugMessage();]
Returns a private character pointer to the last debug message. This debug string is a textual
description of the last operation the machine performed, such as a transition, entry into a
state, or idling in a state.
See \ref{hsmc:options} for information on how to enable debugging information.
\item[virtual void HSMDebug(char* msg);]
This function is empty by default and has no effect. It is called throughout the machine internals
with debug messages; therefore, if you prefer immediate access to these messages, you should override
this function with your own. This allows you to trap debug messages as they happen.
These debug messages are the same as are accessible via HSMGetLastDebugMessage().
See \ref{hsmc:options} for information on how to enable debugging information.
\end{description}
\subsubsection{Action Method Declarations}
Action methods are user defined functions which need to be called by the HSM. These actions
are defined by Entry, Idle, Exit, and Action statements (see \ref{hsm:actions}).
For example, the source input fragment:
\begin{lstlisting}
Entry(MyEntryFunc);
Idle(MyIdleFunc);
Exit(MyExitFunc);
Action(SomeEvent, MySomeEventFunc);
\end{lstlisting}
is compiled to:
\begin{lstlisting}[language=C++]
protected:
// actions
virtual void MyEntryFunc();
virtual void MyIdleFunc();
virtual void MyExitFunc();
virtual void MySomeEventFunc();
\end{lstlisting}
Note that if the pure virtual option is specified (see \ref{hsmc:options}) then these methods
would instead be declared as follows:
\begin{lstlisting}[language=C++]
protected:
// actions
virtual void MyEntryFunc() = 0;
virtual void MyIdleFunc() = 0;
virtual void MyExitFunc() = 0;
virtual void MySomeEventFunc() = 0;
\end{lstlisting}
\subsection{Source File}
The source file contains all of the inner workings of the HSM. In general, you need
know nothing about this file to use \emph{hsmc}. You may wish to browse through it
in case you're curious about how the machines are implemented or if you're debugging
your machine behavior.
\section{Compiler Errors}
You may encounter one or more of the following diagnostic error messages while
compiling with \emph{hsmc}. No output files are generated in the case of any error.
\begin{description}
\item[source line number: Invalid event queue size for Machine 'machinename']
The parser was not able to determine a valid queue size in the
declaration of 'machinename'. See \ref{hsm:machines}.
\item[source line number: State 'statename' was previously defined]
A state named 'statename' was already found in this machine. See \ref{hsm:states}.
\item[source line number: multiple Default states defined in State 'statename']
More than one state defined with the same name, 'statename'. See \ref{hsm:states}.
\item[source line number: multiple Transition's on 'eventname' defined in 'statename']
More than one transition defined for a single event (i.e. ambiguous transition). See \ref{hsm:transitions}.
\item[source line number: multiple Terminate's on 'eventname' defined in 'statename']
More than one terminate defined for a single event. See \ref{hsm:termination}.
\item[ERROR: parser failed]
The parser discovered an unrecoverable syntax error. See \ref{hsm:lexical}.
\item[ERROR: unable to open 'filename']
The compiler was unable to open an input file for reading or an
output file for writing. Make sure the input file exists and is readable and
that the output file either doesn't exist, or is writable.
\item[ERROR: compilation aborted due to errors]
The compiler aborted compilation because of errors. These are most likely
semantic errors and will probably have been printed in addition to this
terminal diagnostic.
\end{description}
\chapter{Implementing a Machine}
Step by step instructions on creating a machine, compiling it,
inheriting it, and running it.
\section{Design}
\section{Compile}
\section{Implement}
\chapter{Examples}
\section{Traffic Signal}
\section{Soda Machine}
\appendix
\chapter{Frequently Asked Questions}
\section{FAQ Category}
\subsection{Question number 1?}
Answer number 1.
\subsection{Question number 2?}
Answer number 2.
\chapter{HSM Language Reference}
\begin{verbatim}
// * A state may contain other states
// * A state may contain Default, Entry, Idle, Exit, Transition, Action, and Terminate
// * A single Default is allowed per state
// * There may only be one transition per event
// * Multiple actions are allowed per event
// * Multiple Entry, Idle, and Exit function calls are allowed
// * Terminate() causes all states to exit, including the root state
// * nested states must be represented hierarchically in input file via {} pairs
// * no empty states
\end{verbatim}
\section{General}
The HSM language attempts to embody the most used, most important features
of hierarchical state machines (as described by \cite{harel:sta}). Some features
from Harel's statecharts are missing from the HSM language, such as historic
transitions.
The language is designed to be easy to edit and manipulate, while remaining
syntactically similar to C++.
\section{Lexical Elements}
\label{hsm:lexical}
\subsection{Character Set}
The HSM source character set consists of 74 characters: the space character, the control characters
representing the horizontal tab, and new-line, plus the following 71 graphical characters:
\begin{verbatim}
A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
a b c d e f g h i j k l m n o p q r s t u v w x y z
0 1 2 3 4 5 6 7 8 9
{ } ( ) , ; . _ /
\end{verbatim}
\subsection{Case Sensitivity}
Keywords are not case sensitive. Therefore, \emph{state}, \emph{STATE}, \emph{State}, and
\emph{StAtE} are all equivalent.
However, identifiers are case sensitive. For example, the HSM in listing \ref{list:casesensitivity} defines two states,
named \emph{S} and \emph{s}; as well as two events, named \emph{E} and \emph{e}.
\begin{lstlisting}[caption={Case Sensitivity on Identifiers},label={list:casesensitivity},float,frame=lines,numbers=left]
Machine(m,16)
{
// s is a default transition
Default(s);
// define state 's'
State(s)
{
// on event 'E', transition to state 'S'
Transition(E,S);
}
// define state 'S'
State(S)
{
// on event 'e', transition to state 's'
Transition(e,s);
}
}
\end{lstlisting}
\subsection{Line Format}
Whitespace is completely ignored; therefore, lines do not require separation by new-line
characters, nor any special formatting with space characters.
All keywords other than \emph{Machine} and \emph{State} require a semicolon.
\subsection{Source Layout}
\label{hsm:file}
Each single HSM source input file must define at least one syntactically correct
Machine (see \ref{hsm:machines}). There is no limit to the number of machines allowed
in a single input file.
\subsection{Keywords}
\label{hsm:keywords}
Exactly nine keywords are defined. They are listed in listing \ref{list:keywords} and
described in detail in sections
\ref{hsm:machines},
\ref{hsm:states},
\ref{hsm:actions},
and
\ref{hsm:transitions},
\begin{lstlisting}[caption={Keywords},label={list:keywords},float,frame=lines]
Machine
State
Default
Entry
Idle
Exit
Transition
Action
Terminate
\end{lstlisting}
\subsection{Identifiers}
Identifiers are used to name Machines, States, and Events. The first character of
an identifier must be one of the 53 graphical characters in the set:
\begin{verbatim}
A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
a b c d e f g h i j k l m n o p q r s t u v w x y z
_
\end{verbatim}
and any characters after the first may be any of the 63 graphical characters in the set:
\begin{verbatim}
A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
a b c d e f g h i j k l m n o p q r s t u v w x y z
_
0 1 2 3 4 5 6 7 8 9
\end{verbatim}
Identifiers longer than 256 characters are not permitted.
Examples of valid and invalid identifiers may be found in listing \ref{list:identifiers}.
\begin{lstlisting}[caption={Identifier Examples},label={list:identifiers},float,frame=lines]
IDENTIFIER
Identifier
identifier
iDeNtifier
_identifier
_000000
_id0
id100
0id // invalid
100id // invalid
\end{lstlisting}
\subsection{Numeric Literals}
Numeric literals are used in the HSM language to specify event queue size (\ref{hsm:machines}) as well as
time values used in timed actions (\ref{hsm:actionstimed})
and timed transitions (\ref{hsm:transitionstimed}).
They may be whole integers or floating point numbers, and must be positive. They must be composed of the digits
0 through 9, and may include a single decimal point denoted by the period character.
\subsection{Reserved Words}
\label{hsm:reserved}
Ten reserved words are defined. They include the word \emph{TOPSTATE} plus the nine keywords defined in \ref{hsm:keywords}.
These words are all reserved and cannot be used as identifiers.
\subsection{Comments}
The characters \emph{//} start a comment, which terminates with the next new-line character.
\section{Scope}
Scope is controlled with the curly brace characters, \emph{\{} and \emph{\}}. These apply
to Machine and State definitions (see \ref{hsm:machines} and \ref{hsm:states}).
This scoping mechanism is what allows for the nesting of states, making these state
machines hierarchical \cite{harel:sta}.
\section{Machines}
\label{hsm:machines}
\begin{description}
\item[Machine(name,queuesize) \{ ... \}] \emph{name} is the unique identifier for this machine; \emph{queuesize}
is the static size of the event queue and must be a
whole integer greater than 0; \emph{...} is the machine definition
which must qualify as a valid state definition (see \ref{hsm:states}).
\end{description}
Machine is declaration and definition of the root state of a Hierarchical State Machine.
Machine is nearly synonymous with State, with the addition of requiring an event queue size
specification. Therefore, everything valid in the definition of a State is also valid in the definition of
a Machine. As such, a Machine definition may contain zero or more substates; however, it may not be empty.
See \ref{hsm:states}.
The state defined by Machine is the root state
in the state hierarchy. It is identified by the reserved identifier \emph{TOPSTATE} (see \ref{hsm:reserved}),
a convention set forth in \cite{samek:psc}.
This automatic identification is necessary to allow explicit transitions to the root state.
It is the first state entered upon machine construction.
\section{States}
\label{hsm:states}
May contain any number of substates.
\section{Actions}
\label{hsm:actions}
\subsection{Normal}
\subsection{Timed}
\label{hsm:actionstimed}
\subsection{Special}
\section{Transitions}
\label{hsm:transitions}
\subsection{Normal}
\subsection{Timed}
\label{hsm:transitionstimed}
\subsection{Default}
\label{hsm:default}
\subsection{Termination}
\label{hsm:termination}
\chapter{HSMC lex and yacc Source}
\section{lex, lexical scanner}
\begin{lstlisting}[language=C,caption={hsmc.l (lex source)},numbers=left,columns=fixed]
%{
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "hsm-parser.tab.h"
int lineno = 1;
void yyerror(char* s);
%}
%%
/* whitespace */
[ \t]+ ; /* skip whitespace */
/* comments */
"//".* ; /* skip C++ comments */
/* punctuation */
"," { return ','; }
";" { return ';'; }
"{" { return '{'; }
"}" { return '}'; }
"(" { return '('; }
")" { return ')'; }
/* keywords */
[Mm][Aa][Cc][Hh][Ii][Nn][Ee] { return MACHINE; }
[Ss][Tt][Aa][Tt][Ee] { return STATE; }
[Dd][Ee][Ff][Aa][Uu][Ll][Tt] { return DEFAULT; }
[Ee][Nn][Tt][Rr][Yy] { return ENTRY; }
[Ii][Dd][Ll][Ee] { return IDLE; }
[Ee][Xx][Ii][Tt] { return EXIT; }
[Tt][Rr][Aa][Nn][Ss][Ii][Tt][Ii][Oo][Nn] { return TRANSITION; }
[Aa][Cc][Tt][Ii][Oo][Nn] { return ACTION; }
[Tt][Ee][Rr][Mm][Ii][Nn][Aa][Tt][Ee] { return TERMINATE; }
/* identifiers */
[a-zA-Z_][a-zA-Z_0-9]* {
if ( strlen(yytext) > 256 )
printf(
"%d: truncating symbol '%s' to 256 characters\n",
lineno,yytext);
strncpy(yylval.string,yytext,256);
return IDENTIFIER;
}
/* constants */
[0-9]+\.?[0-9]* {
yylval.constant = (float)atof(yytext);
return CONSTANT;
}
/* newlines, etc. */
\n { lineno++; }
. ; /* ignore */
%%
int yywrap()
{
return 1;
}
void yyerror(char* s)
{
printf("\n%d: %s at %s\n", lineno, s, yytext);
}
\end{lstlisting}
\section{yacc, parser}
\begin{lstlisting}[language=C,deleteemph={default},caption={hsmc.y (yacc source)},numbers=left,columns=fixed]
%{
extern int yylex();
extern void yyerror(char* s);
#include "main.h"
%}
%union
{
char string[258];
float constant;
}
%token MACHINE
%token STATE
%token DEFAULT
%token ENTRY
%token IDLE
%token EXIT
%token TRANSITION
%token ACTION
%token TERMINATE
%token <string> IDENTIFIER
%token <constant> CONSTANT
%%
machines: machine
| machines machine
;
machine: machine_decl '{' state_items '}'
{
parseEndMachine();
}
;
state_items: state_item
| state_items state_item
;
state_item: state
| statement
;
state: state_decl '{' state_items '}'
{
parseEndState();
}
;
statement: default ';'
| entry ';'
| idle ';'
| exit ';'
| transition ';'
| action ';'
| timetransition ';'
| timeaction ';'
| terminate ';'
;
machine_decl: MACHINE '(' IDENTIFIER ',' CONSTANT ')'
{
parseBeginMachine($3,(int)$5);
}
;
state_decl: STATE '(' IDENTIFIER ')'
{
parseBeginState($3);
}
;
default: DEFAULT '(' IDENTIFIER ')'
{
parseDefault($3);
}
;
entry: ENTRY '(' IDENTIFIER ')'
{
parseEntry($3);
}
;
idle: IDLE '(' IDENTIFIER ')'
{
parseIdle($3);
}
;
exit: EXIT '(' IDENTIFIER ')'
{
parseExit($3);
}
;
transition: TRANSITION '(' IDENTIFIER ',' IDENTIFIER ')'
{
parseTransition($3,$5);
}
;
action: ACTION '(' IDENTIFIER ',' IDENTIFIER ')'
{
parseAction($3,$5);
}
;
timetransition: TRANSITION '(' CONSTANT ',' IDENTIFIER ')'
{
parseTimeTransition($3,$5);
}
;
timeaction: ACTION '(' CONSTANT ',' IDENTIFIER ')'
{
parseTimeAction($3,$5);
}
;
terminate: TERMINATE '(' IDENTIFIER ')'
{
parseTerminate($3);
}
;
\end{lstlisting}
\newpage
\addcontentsline{toc}{chapter}{Glossary}
\chapter*{Glossary}
\begin{description}
\item[blah] A blah is a blah blah blah!\index{blah}
\end{description}
\cleardoublepage
\addcontentsline{toc}{chapter}{Bibliography}
\bibliographystyle{alpha}
\bibliography{doc}
\cleardoublepage
\addcontentsline{toc}{chapter}{Index}
\printindex
\end{document}