commit 0506ec019bfbcd55c452719cb38107d8cfb4af73 Author: Stephen Waits Date: Thu Sep 30 15:17:54 2010 -0700 create hsmc repo diff --git a/.hgignore b/.hgignore new file mode 100644 index 0000000..02774c5 --- /dev/null +++ b/.hgignore @@ -0,0 +1 @@ +.svn\/ diff --git a/TODO b/TODO new file mode 100644 index 0000000..8e04dc2 --- /dev/null +++ b/TODO @@ -0,0 +1,24 @@ + + +TODO List - by Priority +======================= + + +* make sure Update not called from unsafe section (inside machine) + +* add transition guards + adds some complication (not much?) + can you then have multiple transitions per event? I say NO. + needed? + +* Add history? Shallow? Deep? + +* add C-style comment support? + +* change to using text templates for text output??? + +* document language + +* document behavior (such as Transition always causing an exit+entry sequence) + + diff --git a/bin/flex.exe b/bin/flex.exe new file mode 100644 index 0000000..b378ad2 Binary files /dev/null and b/bin/flex.exe differ diff --git a/bin/yacc.exe b/bin/yacc.exe new file mode 100644 index 0000000..a4d68b5 Binary files /dev/null and b/bin/yacc.exe differ diff --git a/example/parsetest/parsetest.hsm b/example/parsetest/parsetest.hsm new file mode 100644 index 0000000..c22c431 --- /dev/null +++ b/example/parsetest/parsetest.hsm @@ -0,0 +1,97 @@ +// TODO: add Timeout mechanism + +// +// * "Machine" is the root state +// * the event queue size is specified in the machine declaration +// * the root state is a state just like any other, nothing special +// * the root state is named TOPSTATE +// * 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 +// * Multiple machines may exist per single input file +// * nested states must be represented hierarchically in input file via {} pairs +// * all keywords are case insensitive +// * keywords are reserved and CANNOT be used for identifiers +// * only C++ style comments are supported +// * C/C++ style source formatting suggested +// * whitespace is completely ignored +// * no empty machines +// * no empty states +// * symbols longer than 256 characters are truncated +// + + +// comment + +Machine(name,16) // Machine is a special alias to State +{ + // this is required in the main state + Default(FirstState); + //Default(seconddefault); + + // this is optional in the main state + Entry(mainentry); + Entry(mainentry2); + Entry(mainentry3); + Idle(blah); + Exit(mainexit); + + sTaTe(FirstState) + { + ENTRY(blah); + idle(blah); + Exit(blah); + + Transition(Event2,SecondState); + //Transition(Event2,xSecondState); + Action(Event,xAction); + + + State(SubState){Idle(blah);State(SubSubState){Idle(ssblah);State(SubSubSubState){Idle(sssblah);}}} + //State(SubState){Idle(blah);State(SubSubState){Idle(ssblah);State(SubSubSubState){Idle(sssblah);}}} + + Terminate(reallyLongSymbolreallyLongSymbolreallyLongSymbolreallyLongSymbolreallyLongSymbolreallyLongSymbolreallyLongSymbolreallyLongSymbolreallyLongSymbolreallyLongSymbolreallyLongSymbolreallyLongSymbolreallyLongSymbolreallyLongSymbolreallyLongSymbolreallyLongSymbolreallyLongSymbolreallyLongSymbolreallyLongSymbolreallyLongSymbolreallyLongSymbolreallyLongSymbolreallyLongSymbolreallyLongSymbolreallyLongSymbolreallyLongSymbolreallyLongSymbolreallyLongSymbolreallyLongSymbolreallyLongSymbolreallyLongSymbolreallyLongSymbolreallyLongSymbolreallyLongSymbolreallyLongSymbolreallyLongSymbolreallyLongSymbolreallyLongSymbolreallyLongSymbolreallyLongSymbolreallyLongSymbolreallyLongSymbolreallyLongSymbolXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX); + + } + + State(SecondState) + { + Idle ( blah ) // + ; + Transition(Event1,FirstState); + //Transition(sldfkj,blkjsdlfkja); + } + + State(xSecondState) + { + Idle(blah); + } + + State(SomeEndState) + { + Entry(blah); + Exit(blah); + Terminate(someevent); + Action(0.23,something); + //Terminate(someevent); + } + + Transition(1,FirstState); + Action(0.99, asldkfj); + Action(0.99, asldkfj1); + Action(0.99, asldkfj2); + //Transition(0., xSecondState); + //Action(0.00001,alsdjf); + +} + +Machine(othermach,1) +{ + Entry(blbla); +} + + diff --git a/example/traffic/main.cpp b/example/traffic/main.cpp new file mode 100644 index 0000000..6d86129 --- /dev/null +++ b/example/traffic/main.cpp @@ -0,0 +1,72 @@ +#include "traffic.h" +#include +#include + +#include // for Sleep() +#include // for kbhit() + + +class MyStoplight: public stoplight +{ +public: + virtual void DoPOST() + { + printf("DoPOST\n"); + } + virtual void CheckForError() + { + if ( kbhit() ) + { + printf("Encountered critical error\n"); + HSMTrigger(FAILURE); + } + } + virtual void GreenOff () + { + printf("Green Off\n"); + } + virtual void GreenOn() + { + printf("Green On\n"); + } + virtual void YellowOff() + { + printf("Yellow Off\n"); + } + virtual void YellowOn() + { + printf("Yellow On\n"); + } + virtual void RedOff() + { + printf("Red Off\n"); + } + virtual void RedOn() + { + printf("Red On\n"); + } + + virtual void EnableRedFlashMode() + { + printf("Setting Red Flash mode\n"); + } + + virtual void HSMDebug(char* msg) + { + printf("HSMDebug: %s\n",msg); + } +}; + +int main() +{ + printf("Press any key to simulate Error.\n"); + + MyStoplight s; + s.HSMConstruct(); + while ( s.HSMUpdate(0.1f) ) + { + Sleep(100); + } + return 0; +} + diff --git a/example/traffic/traffic.hsm b/example/traffic/traffic.hsm new file mode 100644 index 0000000..79a579f --- /dev/null +++ b/example/traffic/traffic.hsm @@ -0,0 +1,47 @@ + +Machine(stoplight,30) +{ + // perform power on self test + Entry(DoPOST); + + // first state is Red + Default(Red); + + // poll for a FAILURE event + Idle(CheckForError); + + // machine exits on FAILURE event + Terminate(FAILURE); + + // if machine exits, switch to Flash Mode + Exit(EnableRedFlashMode); + + // red light state + State(Red) + { + Entry(RedOn); + Exit(RedOff); + + Transition(5.0,Green); + } + + // green light state + State(Green) + { + Entry(GreenOn); + Exit(GreenOff); + + Transition(5,Yellow); + } + + // yellow light state + State(Yellow) + { + Entry(YellowOn); + Exit(YellowOff); + + Transition(1.000000,Red); + } +} + + diff --git a/hsmc.sln b/hsmc.sln new file mode 100644 index 0000000..d21500a --- /dev/null +++ b/hsmc.sln @@ -0,0 +1,32 @@ +Microsoft Visual Studio Solution File, Format Version 8.00 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "hsmc", "hsmc.vcproj", "{C5786482-145A-4B2D-8AA9-2E7CBB1EE69C}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Global + GlobalSection(SourceCodeControl) = preSolution + SccNumberOfProjects = 2 + SccProjectName0 = Perforce\u0020Project + SccLocalPath0 = . + SccProvider0 = MSSCCI:Perforce\u0020SCM + CanCheckoutShared = true + SolutionUniqueID = {BC171BD2-EB06-48AD-A2D5-D4F6E43EA3EF} + SccProjectUniqueName1 = hsmc.vcproj + SccLocalPath1 = . + CanCheckoutShared = true + EndGlobalSection + GlobalSection(SolutionConfiguration) = preSolution + Debug = Debug + Release = Release + EndGlobalSection + GlobalSection(ProjectConfiguration) = postSolution + {C5786482-145A-4B2D-8AA9-2E7CBB1EE69C}.Debug.ActiveCfg = Debug|Win32 + {C5786482-145A-4B2D-8AA9-2E7CBB1EE69C}.Debug.Build.0 = Debug|Win32 + {C5786482-145A-4B2D-8AA9-2E7CBB1EE69C}.Release.ActiveCfg = Release|Win32 + {C5786482-145A-4B2D-8AA9-2E7CBB1EE69C}.Release.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + EndGlobalSection + GlobalSection(ExtensibilityAddIns) = postSolution + EndGlobalSection +EndGlobal diff --git a/hsmc.vcproj b/hsmc.vcproj new file mode 100644 index 0000000..61a734d --- /dev/null +++ b/hsmc.vcproj @@ -0,0 +1,151 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/hsmc.vpj b/hsmc.vpj new file mode 100644 index 0000000..0ee8a34 --- /dev/null +++ b/hsmc.vpj @@ -0,0 +1,98 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/hsmc.vpw b/hsmc.vpw new file mode 100644 index 0000000..32a1617 --- /dev/null +++ b/hsmc.vpw @@ -0,0 +1,6 @@ + + + + + + diff --git a/source/Makefile b/source/Makefile new file mode 100644 index 0000000..6f8f9e8 --- /dev/null +++ b/source/Makefile @@ -0,0 +1,77 @@ +# flags and libs +DEFS = +LIBS = + +# compiler and flags +CC = cc +CCFLAGS = -Wall -O3 -c +CXX = c++ +CXXFLAGS = $(CCFLAGS) +CPPFLAGS = + +# linker +LD = c++ +LDFLAGS = +STRIP = strip + +# misc programs +RM = rm -f +INSTALL = install +MKDEPEND = mkdepend +LEX = flex -f +YACC = yacc + +# our target and source code +TARGET = hsmc +SRCS = hsm-lexer.c hsm-parser.tab.c getopt.cpp main.cpp +OBJS = hsm-lexer.o hsm-parser.tab.o getopt.o main.o + +## +## + +all: $(TARGET) + +$(TARGET): $(OBJS) + $(LD) $(LDFLAGS) $(LIBS) -o $@ $(OBJS) + +hsm-parser.tab.h: \ + hsm.y + +hsm-parser.tab.c: hsm.y + $(YACC) -d -bhsm-parser hsm.y + +hsm-lexer.h: \ + hsm.l + +hsm-lexer.c: hsm.l + $(LEX) -ohsm-lexer.c hsm.l + +depend: + $(MKDEPEND) $(SRCS) + +clean: + $(RM) $(OBJS) $(TARGET) + +.cpp.o: + $(CXX) $(CXXFLAGS) $(CPPFLAGS) $(DEFS) $< -o $@ + +.c.o: + $(CC) $(CCFLAGS) $(CPPFLAGS) $(DEFS) $< -o $@ + + +# DO NOT DELETE THIS LINE -- make depend depends on it. + +hsm-lexer.o: \ + hsm-parser.tab.h + +hsm-parser.o: \ + main.h + +main.o: \ + autofile.h \ + config.h \ + getopt.h \ + machine.h \ + main.h \ + state.h + diff --git a/source/autofile.h b/source/autofile.h new file mode 100644 index 0000000..211831a --- /dev/null +++ b/source/autofile.h @@ -0,0 +1,36 @@ +#ifndef __autofile_h__ +#define __autofile_h__ + +#include + +class AutoFILE +{ +public: + AutoFILE(char* filename, char* mode = "r") + { + f = fopen(filename,mode); + } + ~AutoFILE() + { + if ( Opened() ) + fclose(f); + } + operator FILE* () + { + return f; + } + bool Opened() + { + return f != (FILE*)0; + } + +private: + FILE* f; +}; + + + + + +#endif // __autofile_h__ + diff --git a/source/config.h b/source/config.h new file mode 100644 index 0000000..bf0bc56 --- /dev/null +++ b/source/config.h @@ -0,0 +1,127 @@ +#ifndef __config_h__ +#define __config_h__ + +#include +#include +#include "getopt.h" + +class Config +{ +public: + + std::string InFileName; + std::string OutCPPFileName; + std::string OutHFileName; + std::string IncludeName; + bool Debug; + bool Quiet; + bool PureVirtuals; + + void Usage() + { + printf( + "\n" + "\nHSMC - Hierarchical State Machine Compiler\n" + "\n" + "Usage: hsmc [-hdpq] [-o prefix] source_file\n" + "\n" + " -h this help\n" + " -d include debugging information\n" + " -p actions declared as pure virtual\n" + " -q quiet mode (only output errors)\n" + " -o prefix specify prefix for output files [prefix.cpp,prefix.h]\n" + " source_file HSM source file\n" + "\n" + ); + } + + bool ParseCommandLine(int argc, char* argv[]) + { + // reset defaults + InFileName = ""; + OutCPPFileName = ""; + OutHFileName = ""; + IncludeName = ""; + Debug = false; + Quiet = false; + PureVirtuals = false; + + // temporary + std::string prefix = ""; + + // parse command line + int ch; + while ( (ch = getopt(argc,argv,"hdpqo:")) != -1 ) + { + switch ( ch ) + { + case 'p': + PureVirtuals = true; + break; + + case 'q': + Quiet = true; + break; + + case 'd': + Debug = true; + break; + + case 'o': + prefix = optarg; + break; + + case 'h': + default: + Usage(); + return false; + } + } + + // check for input file + if ( (argc-optind) == 0 ) + { + printf("ERROR: no input file specified\n"); + Usage(); + return false; + } + else if ( (argc-optind) > 1 ) + { + printf("ERROR: multiple input files not allowed\n"); + Usage(); + return false; + } + + // got input file + InFileName = argv[optind]; + + // determine output prefix if it wasn't specified + if ( prefix == "" ) + { + // prefix NOT specified, let's get it from the base + prefix = InFileName.substr(0,InFileName.rfind('.')); + } + + // determine output files specified + OutCPPFileName = prefix + ".cpp"; + OutHFileName = prefix + ".h"; + + // strip up to any '/' or '\' + IncludeName = OutHFileName; + if ( OutHFileName.rfind('/') >= 0 ) + { + IncludeName = IncludeName.substr( IncludeName.rfind('/')+1 ); + } + if ( OutHFileName.rfind('\\') >= 0 ) + { + IncludeName = IncludeName.substr( IncludeName.rfind('\\')+1 ); + } + + // success + return true; + } +}; + + +#endif // __config_h__ + diff --git a/source/getopt.cpp b/source/getopt.cpp new file mode 100644 index 0000000..237fbdf --- /dev/null +++ b/source/getopt.cpp @@ -0,0 +1,138 @@ +/* + * Copyright (c) 1987, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#if defined(LIBC_SCCS) && !defined(lint) + #if 0 + static char sccsid[] = "@(#)getopt.c 8.3 (Berkeley) 4/27/95"; + #endif + static const char rcsid[] = "$FreeBSD: src/lib/libc/stdlib/getopt.c,v 1.2 1999/12/17 01:52:15 jkh Exp $"; +#endif /* LIBC_SCCS and not lint */ + +#include +#include +#include + +int opterr = 1; /* if error message should be printed */ +int optind = 1; /* index into parent argv vector */ +int optopt; /* character checked for validity */ +int optreset; /* reset getopt */ +char *optarg; /* argument associated with option */ + + +#define BADCH (int)'?' +#define BADARG (int)':' +#define EMSG "" + +/* +* getopt -- +* Parse argc/argv argument vector. +*/ +#if defined(__cplusplus) +extern "C" +#endif +int getopt(int nargc, char* const* nargv, const char* ostr) +{ + char *__progname = nargv[0]; + static char *place = EMSG; /* option letter processing */ + char *oli; /* option letter list index */ + int ret; + + if ( optreset || !*place ) + { /* update scanning pointer */ + optreset = 0; + if ( optind >= nargc || *(place = nargv[optind]) != '-' ) + { + place = EMSG; + return(-1); + } + if ( place[1] && *++place == '-' ) + { /* found "--" */ + ++optind; + place = EMSG; + return(-1); + } + } /* option letter okay? */ + if ( (optopt = (int)*place++) == (int)':' || !(oli = strchr(ostr, optopt)) ) + { + /* + * if the user didn't specify '-' as an option, + * assume it means -1. + */ + if ( optopt == (int)'-' ) + return(-1); + if ( !*place ) + ++optind; + if ( opterr && *ostr != ':' ) + (void)fprintf(stderr,"%s: illegal option -- %c\n", __progname, optopt); + return(BADCH); + } + if ( *++oli != ':' ) + { + /* don't need argument */ + optarg = NULL; + if ( !*place ) + { + ++optind; + } + } + else + { + /* need an argument */ + if ( *place ) + { + /* no white space */ + optarg = place; + } + else if ( nargc <= ++optind ) + { + /* no arg */ + place = EMSG; + if ( *ostr == ':' ) + ret = BADARG; + else + ret = BADCH; + if ( opterr ) + (void)fprintf(stderr,"%s: option requires an argument -- %c\n",__progname, optopt); + return(ret); + } + else + { + /* white space */ + optarg = nargv[optind]; + } + place = EMSG; + ++optind; + } + return(optopt); /* dump back option letter */ +} + diff --git a/source/getopt.h b/source/getopt.h new file mode 100644 index 0000000..2304d6f --- /dev/null +++ b/source/getopt.h @@ -0,0 +1,20 @@ +#ifndef __getopt_h__ +#define __getopt_h__ + +extern int opterr; /* if error message should be printed */ +extern int optind; /* index into parent argv vector */ +extern int optopt; /* character checked for validity */ +extern int optreset; /* reset getopt */ +extern char *optarg; /* argument associated with option */ + +#if defined(__cplusplus) +extern "C" { +#endif + +int getopt(int nargc, char* const* nargv, const char* ostr); + +#if defined(__cplusplus) +} +#endif + +#endif // __getopt_h__ diff --git a/source/hsm-lexer.c b/source/hsm-lexer.c new file mode 100644 index 0000000..dc3e0c0 --- /dev/null +++ b/source/hsm-lexer.c @@ -0,0 +1,1684 @@ +#line 2 "hsm-lexer.c" +/* A lexical scanner generated by flex */ + +/* Scanner skeleton version: + * $Header: //shared/tools/util/hsmc/current/source/hsm-lexer.c#1 $ + */ + +#define FLEX_SCANNER +#define YY_FLEX_MAJOR_VERSION 2 +#define YY_FLEX_MINOR_VERSION 5 + +#include + + +/* cfront 1.2 defines "c_plusplus" instead of "__cplusplus" */ +#ifdef c_plusplus +#ifndef __cplusplus +#define __cplusplus +#endif +#endif + + +#ifdef __cplusplus + +#include +#include + +/* Use prototypes in function declarations. */ +#define YY_USE_PROTOS + +/* The "const" storage-class-modifier is valid. */ +#define YY_USE_CONST + +#else /* ! __cplusplus */ + +#if __STDC__ + +#define YY_USE_PROTOS +#define YY_USE_CONST + +#endif /* __STDC__ */ +#endif /* ! __cplusplus */ + +#ifdef __TURBOC__ + #pragma warn -rch + #pragma warn -use +#include +#include +#define YY_USE_CONST +#define YY_USE_PROTOS +#endif + +#ifdef YY_USE_CONST +#define yyconst const +#else +#define yyconst +#endif + + +#ifdef YY_USE_PROTOS +#define YY_PROTO(proto) proto +#else +#define YY_PROTO(proto) () +#endif + +/* Returned upon end-of-file. */ +#define YY_NULL 0 + +/* Promotes a possibly negative, possibly signed char to an unsigned + * integer for use as an array index. If the signed char is negative, + * we want to instead treat it as an 8-bit unsigned char, hence the + * double cast. + */ +#define YY_SC_TO_UI(c) ((unsigned int) (unsigned char) c) + +/* Enter a start condition. This macro really ought to take a parameter, + * but we do it the disgusting crufty way forced on us by the ()-less + * definition of BEGIN. + */ +#define BEGIN yy_start = 1 + 2 * + +/* Translate the current start state into a value that can be later handed + * to BEGIN to return to the state. The YYSTATE alias is for lex + * compatibility. + */ +#define YY_START ((yy_start - 1) / 2) +#define YYSTATE YY_START + +/* Action number for EOF rule of a given start state. */ +#define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1) + +/* Special action meaning "start processing a new file". */ +#define YY_NEW_FILE yyrestart( yyin ) + +#define YY_END_OF_BUFFER_CHAR 0 + +/* Size of default input buffer. */ +#define YY_BUF_SIZE 16384 + +typedef struct yy_buffer_state *YY_BUFFER_STATE; + +extern int yyleng; +extern FILE *yyin, *yyout; + +#define EOB_ACT_CONTINUE_SCAN 0 +#define EOB_ACT_END_OF_FILE 1 +#define EOB_ACT_LAST_MATCH 2 + +/* The funky do-while in the following #define is used to turn the definition + * int a single C statement (which needs a semi-colon terminator). This + * avoids problems with code like: + * + * if ( condition_holds ) + * yyless( 5 ); + * else + * do_something_else(); + * + * Prior to using the do-while the compiler would get upset at the + * "else" because it interpreted the "if" statement as being all + * done when it reached the ';' after the yyless() call. + */ + +/* Return all but the first 'n' matched characters back to the input stream. */ + +#define yyless(n) \ + do \ + { \ + /* Undo effects of setting up yytext. */ \ + *yy_cp = yy_hold_char; \ + YY_RESTORE_YY_MORE_OFFSET \ + yy_c_buf_p = yy_cp = yy_bp + n - YY_MORE_ADJ; \ + YY_DO_BEFORE_ACTION; /* set up yytext again */ \ + } \ + while ( 0 ) + +#define unput(c) yyunput( c, yytext_ptr ) + +/* The following is because we cannot portably get our hands on size_t + * (without autoconf's help, which isn't available because we want + * flex-generated scanners to compile on their own). + */ +typedef unsigned int yy_size_t; + + +struct yy_buffer_state + { + FILE *yy_input_file; + + char *yy_ch_buf; /* input buffer */ + char *yy_buf_pos; /* current position in input buffer */ + + /* Size of input buffer in bytes, not including room for EOB + * characters. + */ + yy_size_t yy_buf_size; + + /* Number of characters read into yy_ch_buf, not including EOB + * characters. + */ + int yy_n_chars; + + /* Whether we "own" the buffer - i.e., we know we created it, + * and can realloc() it to grow it, and should free() it to + * delete it. + */ + int yy_is_our_buffer; + + /* Whether this is an "interactive" input source; if so, and + * if we're using stdio for input, then we want to use getc() + * instead of fread(), to make sure we stop fetching input after + * each newline. + */ + int yy_is_interactive; + + /* Whether we're considered to be at the beginning of a line. + * If so, '^' rules will be active on the next match, otherwise + * not. + */ + int yy_at_bol; + + /* Whether to try to fill the input buffer when we reach the + * end of it. + */ + int yy_fill_buffer; + + int yy_buffer_status; +#define YY_BUFFER_NEW 0 +#define YY_BUFFER_NORMAL 1 + /* When an EOF's been seen but there's still some text to process + * then we mark the buffer as YY_EOF_PENDING, to indicate that we + * shouldn't try reading from the input source any more. We might + * still have a bunch of tokens to match, though, because of + * possible backing-up. + * + * When we actually see the EOF, we change the status to "new" + * (via yyrestart()), so that the user can continue scanning by + * just pointing yyin at a new input file. + */ +#define YY_BUFFER_EOF_PENDING 2 + }; + +static YY_BUFFER_STATE yy_current_buffer = 0; + +/* We provide macros for accessing buffer states in case in the + * future we want to put the buffer states in a more general + * "scanner state". + */ +#define YY_CURRENT_BUFFER yy_current_buffer + + +/* yy_hold_char holds the character lost when yytext is formed. */ +static char yy_hold_char; + +static int yy_n_chars; /* number of characters read into yy_ch_buf */ + + +int yyleng; + +/* Points to current character in buffer. */ +static char *yy_c_buf_p = (char *) 0; +static int yy_init = 1; /* whether we need to initialize */ +static int yy_start = 0; /* start state number */ + +/* Flag which is used to allow yywrap()'s to do buffer switches + * instead of setting up a fresh yyin. A bit of a hack ... + */ +static int yy_did_buffer_switch_on_eof; + +void yyrestart YY_PROTO(( FILE *input_file )); + +void yy_switch_to_buffer YY_PROTO(( YY_BUFFER_STATE new_buffer )); +void yy_load_buffer_state YY_PROTO(( void )); +YY_BUFFER_STATE yy_create_buffer YY_PROTO(( FILE *file, int size )); +void yy_delete_buffer YY_PROTO(( YY_BUFFER_STATE b )); +void yy_init_buffer YY_PROTO(( YY_BUFFER_STATE b, FILE *file )); +void yy_flush_buffer YY_PROTO(( YY_BUFFER_STATE b )); +#define YY_FLUSH_BUFFER yy_flush_buffer( yy_current_buffer ) + +YY_BUFFER_STATE yy_scan_buffer YY_PROTO(( char *base, yy_size_t size )); +YY_BUFFER_STATE yy_scan_string YY_PROTO(( yyconst char *yy_str )); +YY_BUFFER_STATE yy_scan_bytes YY_PROTO(( yyconst char *bytes, int len )); + +static void *yy_flex_alloc YY_PROTO(( yy_size_t )); +static void *yy_flex_realloc YY_PROTO(( void *, yy_size_t )); +static void yy_flex_free YY_PROTO(( void * )); + +#define yy_new_buffer yy_create_buffer + +#define yy_set_interactive(is_interactive) \ + { \ + if ( ! yy_current_buffer ) \ + yy_current_buffer = yy_create_buffer( yyin, YY_BUF_SIZE ); \ + yy_current_buffer->yy_is_interactive = is_interactive; \ + } + +#define yy_set_bol(at_bol) \ + { \ + if ( ! yy_current_buffer ) \ + yy_current_buffer = yy_create_buffer( yyin, YY_BUF_SIZE ); \ + yy_current_buffer->yy_at_bol = at_bol; \ + } + +#define YY_AT_BOL() (yy_current_buffer->yy_at_bol) + +typedef unsigned char YY_CHAR; +FILE *yyin = (FILE *) 0, *yyout = (FILE *) 0; +typedef int yy_state_type; +extern char *yytext; +#define yytext_ptr yytext + +static yy_state_type yy_get_previous_state YY_PROTO(( void )); +static yy_state_type yy_try_NUL_trans YY_PROTO(( yy_state_type current_state )); +static int yy_get_next_buffer YY_PROTO(( void )); +static void yy_fatal_error YY_PROTO(( yyconst char msg[] )); + +/* Done after the current pattern has been matched and before the + * corresponding action - sets up yytext. + */ +#define YY_DO_BEFORE_ACTION \ + yytext_ptr = yy_bp; \ + yyleng = (int) (yy_cp - yy_bp); \ + yy_hold_char = *yy_cp; \ + *yy_cp = '\0'; \ + yy_c_buf_p = yy_cp; + +#define YY_NUM_RULES 22 +#define YY_END_OF_BUFFER 23 +static yyconst short int yy_accept[79] = + { 0, + 0, 0, 23, 21, 1, 20, 7, 8, 3, 21, + 19, 4, 18, 18, 18, 18, 18, 18, 18, 18, + 5, 6, 1, 2, 19, 19, 18, 18, 18, 18, + 18, 18, 18, 18, 18, 18, 2, 19, 18, 18, + 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, + 14, 13, 18, 18, 18, 18, 18, 18, 12, 18, + 10, 18, 18, 16, 18, 18, 18, 18, 11, 9, + 18, 18, 18, 18, 17, 18, 15, 0 + } ; + +static yyconst int yy_ec[256] = + { 0, + 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 2, 1, 1, 1, 1, 1, 1, 1, 4, + 5, 1, 1, 6, 1, 7, 8, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 1, 10, 1, + 1, 1, 1, 1, 11, 12, 13, 14, 15, 16, + 12, 17, 18, 12, 12, 19, 20, 21, 22, 12, + 12, 23, 24, 25, 26, 12, 12, 27, 28, 12, + 1, 1, 1, 1, 12, 1, 11, 12, 13, 14, + + 15, 16, 12, 17, 18, 12, 12, 19, 20, 21, + 22, 12, 12, 23, 24, 25, 26, 12, 12, 27, + 28, 12, 29, 1, 30, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1 + } ; + +static yyconst int yy_meta[31] = + { 0, + 1, 1, 2, 1, 1, 1, 1, 1, 3, 1, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 1, 1 + } ; + +static yyconst short int yy_base[81] = + { 0, + 0, 0, 91, 92, 88, 92, 92, 92, 92, 81, + 24, 92, 75, 0, 72, 11, 72, 74, 59, 19, + 92, 92, 81, 0, 73, 28, 0, 56, 64, 54, + 60, 58, 63, 64, 51, 62, 0, 63, 53, 59, + 46, 43, 52, 49, 40, 44, 42, 40, 35, 32, + 0, 0, 41, 43, 39, 32, 34, 35, 0, 32, + 0, 31, 33, 0, 25, 34, 37, 22, 0, 0, + 21, 26, 26, 18, 0, 18, 0, 92, 33, 42 + } ; + +static yyconst short int yy_def[81] = + { 0, + 78, 1, 78, 78, 78, 78, 78, 78, 78, 78, + 78, 78, 79, 79, 79, 79, 79, 79, 79, 79, + 78, 78, 78, 80, 78, 78, 79, 79, 79, 79, + 79, 79, 79, 79, 79, 79, 80, 78, 79, 79, + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, + 79, 79, 79, 79, 79, 79, 79, 0, 78, 78 + } ; + +static yyconst short int yy_nxt[123] = + { 0, + 4, 5, 6, 7, 8, 9, 4, 10, 11, 12, + 13, 14, 14, 15, 16, 14, 14, 17, 14, 18, + 14, 14, 14, 19, 20, 14, 14, 14, 21, 22, + 25, 30, 26, 35, 25, 27, 26, 31, 77, 76, + 75, 36, 37, 74, 37, 73, 72, 71, 70, 69, + 68, 67, 66, 65, 64, 63, 62, 61, 60, 59, + 58, 57, 56, 55, 54, 53, 52, 51, 50, 49, + 48, 38, 47, 46, 45, 44, 43, 42, 41, 40, + 39, 38, 23, 34, 33, 32, 29, 28, 24, 23, + 78, 3, 78, 78, 78, 78, 78, 78, 78, 78, + + 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, + 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, + 78, 78 + } ; + +static yyconst short int yy_chk[123] = + { 0, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 11, 16, 11, 20, 26, 79, 26, 16, 76, 74, + 73, 20, 80, 72, 80, 71, 68, 67, 66, 65, + 63, 62, 60, 58, 57, 56, 55, 54, 53, 50, + 49, 48, 47, 46, 45, 44, 43, 42, 41, 40, + 39, 38, 36, 35, 34, 33, 32, 31, 30, 29, + 28, 25, 23, 19, 18, 17, 15, 13, 10, 5, + 3, 78, 78, 78, 78, 78, 78, 78, 78, 78, + + 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, + 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, + 78, 78 + } ; + +static yy_state_type yy_last_accepting_state; +static char *yy_last_accepting_cpos; + +/* The intent behind this definition is that it'll catch + * any uses of REJECT which flex missed. + */ +#define REJECT reject_used_but_not_detected +#define yymore() yymore_used_but_not_detected +#define YY_MORE_ADJ 0 +#define YY_RESTORE_YY_MORE_OFFSET +char *yytext; +#line 1 "hsm.l" +#define INITIAL 0 +#line 2 "hsm.l" + + +#include +#include +#include + +#include "hsm-parser.tab.h" + +int lineno = 1; + +void yyerror(char* s); + + +#line 427 "hsm-lexer.c" + +/* Macros after this point can all be overridden by user definitions in + * section 1. + */ + +#ifndef YY_SKIP_YYWRAP +#ifdef __cplusplus +extern "C" int yywrap YY_PROTO(( void )); +#else +extern int yywrap YY_PROTO(( void )); +#endif +#endif + +#ifndef YY_NO_UNPUT +static void yyunput YY_PROTO(( int c, char *buf_ptr )); +#endif + +#ifndef yytext_ptr +static void yy_flex_strncpy YY_PROTO(( char *, yyconst char *, int )); +#endif + +#ifdef YY_NEED_STRLEN +static int yy_flex_strlen YY_PROTO(( yyconst char * )); +#endif + +#ifndef YY_NO_INPUT +#ifdef __cplusplus +static int yyinput YY_PROTO(( void )); +#else +static int input YY_PROTO(( void )); +#endif +#endif + +#if YY_STACK_USED +static int yy_start_stack_ptr = 0; +static int yy_start_stack_depth = 0; +static int *yy_start_stack = 0; +#ifndef YY_NO_PUSH_STATE +static void yy_push_state YY_PROTO(( int new_state )); +#endif +#ifndef YY_NO_POP_STATE +static void yy_pop_state YY_PROTO(( void )); +#endif +#ifndef YY_NO_TOP_STATE +static int yy_top_state YY_PROTO(( void )); +#endif + +#else +#define YY_NO_PUSH_STATE 1 +#define YY_NO_POP_STATE 1 +#define YY_NO_TOP_STATE 1 +#endif + +#ifdef YY_MALLOC_DECL +YY_MALLOC_DECL +#else +#if __STDC__ +#ifndef __cplusplus +#include +#endif +#else +/* Just try to get by without declaring the routines. This will fail + * miserably on non-ANSI systems for which sizeof(size_t) != sizeof(int) + * or sizeof(void*) != sizeof(int). + */ +#endif +#endif + +/* Amount of stuff to slurp up with each read. */ +#ifndef YY_READ_BUF_SIZE +#define YY_READ_BUF_SIZE 8192 +#endif + +/* Copy whatever the last rule matched to the standard output. */ + +#ifndef ECHO +/* This used to be an fputs(), but since the string might contain NUL's, + * we now use fwrite(). + */ +#define ECHO (void) fwrite( yytext, yyleng, 1, yyout ) +#endif + +/* Gets input and stuffs it into "buf". number of characters read, or YY_NULL, + * is returned in "result". + */ +#ifndef YY_INPUT +#define YY_INPUT(buf,result,max_size) \ + if ( yy_current_buffer->yy_is_interactive ) \ + { \ + int c = '*', n; \ + for ( n = 0; n < max_size && \ + (c = getc( yyin )) != EOF && c != '\n'; ++n ) \ + buf[n] = (char) c; \ + if ( c == '\n' ) \ + buf[n++] = (char) c; \ + if ( c == EOF && ferror( yyin ) ) \ + YY_FATAL_ERROR( "input in flex scanner failed" ); \ + result = n; \ + } \ + else if ( ((result = fread( buf, 1, max_size, yyin )) == 0) \ + && ferror( yyin ) ) \ + YY_FATAL_ERROR( "input in flex scanner failed" ); +#endif + +/* No semi-colon after return; correct usage is to write "yyterminate();" - + * we don't want an extra ';' after the "return" because that will cause + * some compilers to complain about unreachable statements. + */ +#ifndef yyterminate +#define yyterminate() return YY_NULL +#endif + +/* Number of entries by which start-condition stack grows. */ +#ifndef YY_START_STACK_INCR +#define YY_START_STACK_INCR 25 +#endif + +/* Report a fatal error. */ +#ifndef YY_FATAL_ERROR +#define YY_FATAL_ERROR(msg) yy_fatal_error( msg ) +#endif + +/* Default declaration of generated scanner - a define so the user can + * easily add parameters. + */ +#ifndef YY_DECL +#define YY_DECL int yylex YY_PROTO(( void )) +#endif + +/* Code executed at the beginning of each rule, after yytext and yyleng + * have been set up. + */ +#ifndef YY_USER_ACTION +#define YY_USER_ACTION +#endif + +/* Code executed at the end of each rule. */ +#ifndef YY_BREAK +#define YY_BREAK break; +#endif + +#define YY_RULE_SETUP \ + YY_USER_ACTION + +YY_DECL + { + register yy_state_type yy_current_state; + register char *yy_cp, *yy_bp; + register int yy_act; + +#line 18 "hsm.l" + + + /* whitespace */ + +#line 583 "hsm-lexer.c" + + if ( yy_init ) + { + yy_init = 0; + +#ifdef YY_USER_INIT + YY_USER_INIT; +#endif + + if ( ! yy_start ) + yy_start = 1; /* first start state */ + + if ( ! yyin ) + yyin = stdin; + + if ( ! yyout ) + yyout = stdout; + + if ( ! yy_current_buffer ) + yy_current_buffer = + yy_create_buffer( yyin, YY_BUF_SIZE ); + + yy_load_buffer_state(); + } + + while ( 1 ) /* loops until end-of-file is reached */ + { + yy_cp = yy_c_buf_p; + + /* Support of yytext. */ + *yy_cp = yy_hold_char; + + /* yy_bp points to the position in yy_ch_buf of the start of + * the current run. + */ + yy_bp = yy_cp; + + yy_current_state = yy_start; +yy_match: + do + { + register YY_CHAR yy_c = yy_ec[YY_SC_TO_UI(*yy_cp)]; + if ( yy_accept[yy_current_state] ) + { + yy_last_accepting_state = yy_current_state; + yy_last_accepting_cpos = yy_cp; + } + while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) + { + yy_current_state = (int) yy_def[yy_current_state]; + if ( yy_current_state >= 79 ) + yy_c = yy_meta[(unsigned int) yy_c]; + } + yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c]; + ++yy_cp; + } + while ( yy_base[yy_current_state] != 92 ); + +yy_find_action: + yy_act = yy_accept[yy_current_state]; + if ( yy_act == 0 ) + { /* have to back up */ + yy_cp = yy_last_accepting_cpos; + yy_current_state = yy_last_accepting_state; + yy_act = yy_accept[yy_current_state]; + } + + YY_DO_BEFORE_ACTION; + + +do_action: /* This label is used only to access EOF actions. */ + + + switch ( yy_act ) + { /* beginning of action switch */ + case 0: /* must back up */ + /* undo the effects of YY_DO_BEFORE_ACTION */ + *yy_cp = yy_hold_char; + yy_cp = yy_last_accepting_cpos; + yy_current_state = yy_last_accepting_state; + goto yy_find_action; + +case 1: +YY_RULE_SETUP +#line 22 "hsm.l" +; /* skip whitespace */ + YY_BREAK +/* comments */ +case 2: +YY_RULE_SETUP +#line 28 "hsm.l" +; /* skip C++ comments */ + YY_BREAK +/* punctuation */ +case 3: +YY_RULE_SETUP +#line 33 "hsm.l" +{ return ','; } + YY_BREAK +case 4: +YY_RULE_SETUP +#line 34 "hsm.l" +{ return ';'; } + YY_BREAK +case 5: +YY_RULE_SETUP +#line 35 "hsm.l" +{ return '{'; } + YY_BREAK +case 6: +YY_RULE_SETUP +#line 36 "hsm.l" +{ return '}'; } + YY_BREAK +case 7: +YY_RULE_SETUP +#line 37 "hsm.l" +{ return '('; } + YY_BREAK +case 8: +YY_RULE_SETUP +#line 38 "hsm.l" +{ return ')'; } + YY_BREAK +/* keywords */ +case 9: +YY_RULE_SETUP +#line 43 "hsm.l" +{ return MACHINE; } + YY_BREAK +case 10: +YY_RULE_SETUP +#line 44 "hsm.l" +{ return STATE; } + YY_BREAK +case 11: +YY_RULE_SETUP +#line 45 "hsm.l" +{ return DEFAULT; } + YY_BREAK +case 12: +YY_RULE_SETUP +#line 46 "hsm.l" +{ return ENTRY; } + YY_BREAK +case 13: +YY_RULE_SETUP +#line 47 "hsm.l" +{ return IDLE; } + YY_BREAK +case 14: +YY_RULE_SETUP +#line 48 "hsm.l" +{ return EXIT; } + YY_BREAK +case 15: +YY_RULE_SETUP +#line 49 "hsm.l" +{ return TRANSITION; } + YY_BREAK +case 16: +YY_RULE_SETUP +#line 50 "hsm.l" +{ return ACTION; } + YY_BREAK +case 17: +YY_RULE_SETUP +#line 51 "hsm.l" +{ return TERMINATE; } + YY_BREAK +/* identifiers */ +case 18: +YY_RULE_SETUP +#line 56 "hsm.l" +{ if ( strlen(yytext) > 256 ) printf("%d: truncating symbol '%s' to 256 characters\n",lineno,yytext); strncpy(yylval.string,yytext,256); return IDENTIFIER; } + YY_BREAK +/* constants */ +case 19: +YY_RULE_SETUP +#line 61 "hsm.l" +{ yylval.constant = (float)atof(yytext); return CONSTANT; } + YY_BREAK +/* newlines, etc. */ +case 20: +YY_RULE_SETUP +#line 66 "hsm.l" +{ lineno++; } + YY_BREAK +case 21: +YY_RULE_SETUP +#line 67 "hsm.l" +; /* ignore */ + YY_BREAK +case 22: +YY_RULE_SETUP +#line 70 "hsm.l" +ECHO; + YY_BREAK +#line 782 "hsm-lexer.c" +case YY_STATE_EOF(INITIAL): + yyterminate(); + + case YY_END_OF_BUFFER: + { + /* Amount of text matched not including the EOB char. */ + int yy_amount_of_matched_text = (int) (yy_cp - yytext_ptr) - 1; + + /* Undo the effects of YY_DO_BEFORE_ACTION. */ + *yy_cp = yy_hold_char; + YY_RESTORE_YY_MORE_OFFSET + + if ( yy_current_buffer->yy_buffer_status == YY_BUFFER_NEW ) + { + /* We're scanning a new file or input source. It's + * possible that this happened because the user + * just pointed yyin at a new source and called + * yylex(). If so, then we have to assure + * consistency between yy_current_buffer and our + * globals. Here is the right place to do so, because + * this is the first action (other than possibly a + * back-up) that will match for the new input source. + */ + yy_n_chars = yy_current_buffer->yy_n_chars; + yy_current_buffer->yy_input_file = yyin; + yy_current_buffer->yy_buffer_status = YY_BUFFER_NORMAL; + } + + /* Note that here we test for yy_c_buf_p "<=" to the position + * of the first EOB in the buffer, since yy_c_buf_p will + * already have been incremented past the NUL character + * (since all states make transitions on EOB to the + * end-of-buffer state). Contrast this with the test + * in input(). + */ + if ( yy_c_buf_p <= &yy_current_buffer->yy_ch_buf[yy_n_chars] ) + { /* This was really a NUL. */ + yy_state_type yy_next_state; + + yy_c_buf_p = yytext_ptr + yy_amount_of_matched_text; + + yy_current_state = yy_get_previous_state(); + + /* Okay, we're now positioned to make the NUL + * transition. We couldn't have + * yy_get_previous_state() go ahead and do it + * for us because it doesn't know how to deal + * with the possibility of jamming (and we don't + * want to build jamming into it because then it + * will run more slowly). + */ + + yy_next_state = yy_try_NUL_trans( yy_current_state ); + + yy_bp = yytext_ptr + YY_MORE_ADJ; + + if ( yy_next_state ) + { + /* Consume the NUL. */ + yy_cp = ++yy_c_buf_p; + yy_current_state = yy_next_state; + goto yy_match; + } + + else + { + yy_cp = yy_c_buf_p; + goto yy_find_action; + } + } + + else switch ( yy_get_next_buffer() ) + { + case EOB_ACT_END_OF_FILE: + { + yy_did_buffer_switch_on_eof = 0; + + if ( yywrap() ) + { + /* Note: because we've taken care in + * yy_get_next_buffer() to have set up + * yytext, we can now set up + * yy_c_buf_p so that if some total + * hoser (like flex itself) wants to + * call the scanner after we return the + * YY_NULL, it'll still work - another + * YY_NULL will get returned. + */ + yy_c_buf_p = yytext_ptr + YY_MORE_ADJ; + + yy_act = YY_STATE_EOF(YY_START); + goto do_action; + } + + else + { + if ( ! yy_did_buffer_switch_on_eof ) + YY_NEW_FILE; + } + break; + } + + case EOB_ACT_CONTINUE_SCAN: + yy_c_buf_p = + yytext_ptr + yy_amount_of_matched_text; + + yy_current_state = yy_get_previous_state(); + + yy_cp = yy_c_buf_p; + yy_bp = yytext_ptr + YY_MORE_ADJ; + goto yy_match; + + case EOB_ACT_LAST_MATCH: + yy_c_buf_p = + &yy_current_buffer->yy_ch_buf[yy_n_chars]; + + yy_current_state = yy_get_previous_state(); + + yy_cp = yy_c_buf_p; + yy_bp = yytext_ptr + YY_MORE_ADJ; + goto yy_find_action; + } + break; + } + + default: + YY_FATAL_ERROR( + "fatal flex scanner internal error--no action found" ); + } /* end of action switch */ + } /* end of scanning one token */ + } /* end of yylex */ + + +/* yy_get_next_buffer - try to read in a new buffer + * + * Returns a code representing an action: + * EOB_ACT_LAST_MATCH - + * EOB_ACT_CONTINUE_SCAN - continue scanning from current position + * EOB_ACT_END_OF_FILE - end of file + */ + +static int yy_get_next_buffer() + { + register char *dest = yy_current_buffer->yy_ch_buf; + register char *source = yytext_ptr; + register int number_to_move, i; + int ret_val; + + if ( yy_c_buf_p > &yy_current_buffer->yy_ch_buf[yy_n_chars + 1] ) + YY_FATAL_ERROR( + "fatal flex scanner internal error--end of buffer missed" ); + + if ( yy_current_buffer->yy_fill_buffer == 0 ) + { /* Don't try to fill the buffer, so this is an EOF. */ + if ( yy_c_buf_p - yytext_ptr - YY_MORE_ADJ == 1 ) + { + /* We matched a single character, the EOB, so + * treat this as a final EOF. + */ + return EOB_ACT_END_OF_FILE; + } + + else + { + /* We matched some text prior to the EOB, first + * process it. + */ + return EOB_ACT_LAST_MATCH; + } + } + + /* Try to read more data. */ + + /* First move last chars to start of buffer. */ + number_to_move = (int) (yy_c_buf_p - yytext_ptr) - 1; + + for ( i = 0; i < number_to_move; ++i ) + *(dest++) = *(source++); + + if ( yy_current_buffer->yy_buffer_status == YY_BUFFER_EOF_PENDING ) + /* don't do the read, it's not guaranteed to return an EOF, + * just force an EOF + */ + yy_current_buffer->yy_n_chars = yy_n_chars = 0; + + else + { + int num_to_read = + yy_current_buffer->yy_buf_size - number_to_move - 1; + + while ( num_to_read <= 0 ) + { /* Not enough room in the buffer - grow it. */ +#ifdef YY_USES_REJECT + YY_FATAL_ERROR( +"input buffer overflow, can't enlarge buffer because scanner uses REJECT" ); +#else + + /* just a shorter name for the current buffer */ + YY_BUFFER_STATE b = yy_current_buffer; + + int yy_c_buf_p_offset = + (int) (yy_c_buf_p - b->yy_ch_buf); + + if ( b->yy_is_our_buffer ) + { + int new_size = b->yy_buf_size * 2; + + if ( new_size <= 0 ) + b->yy_buf_size += b->yy_buf_size / 8; + else + b->yy_buf_size *= 2; + + b->yy_ch_buf = (char *) + /* Include room in for 2 EOB chars. */ + yy_flex_realloc( (void *) b->yy_ch_buf, + b->yy_buf_size + 2 ); + } + else + /* Can't grow it, we don't own it. */ + b->yy_ch_buf = 0; + + if ( ! b->yy_ch_buf ) + YY_FATAL_ERROR( + "fatal error - scanner input buffer overflow" ); + + yy_c_buf_p = &b->yy_ch_buf[yy_c_buf_p_offset]; + + num_to_read = yy_current_buffer->yy_buf_size - + number_to_move - 1; +#endif + } + + if ( num_to_read > YY_READ_BUF_SIZE ) + num_to_read = YY_READ_BUF_SIZE; + + /* Read in more data. */ + YY_INPUT( (&yy_current_buffer->yy_ch_buf[number_to_move]), + yy_n_chars, num_to_read ); + + yy_current_buffer->yy_n_chars = yy_n_chars; + } + + if ( yy_n_chars == 0 ) + { + if ( number_to_move == YY_MORE_ADJ ) + { + ret_val = EOB_ACT_END_OF_FILE; + yyrestart( yyin ); + } + + else + { + ret_val = EOB_ACT_LAST_MATCH; + yy_current_buffer->yy_buffer_status = + YY_BUFFER_EOF_PENDING; + } + } + + else + ret_val = EOB_ACT_CONTINUE_SCAN; + + yy_n_chars += number_to_move; + yy_current_buffer->yy_ch_buf[yy_n_chars] = YY_END_OF_BUFFER_CHAR; + yy_current_buffer->yy_ch_buf[yy_n_chars + 1] = YY_END_OF_BUFFER_CHAR; + + yytext_ptr = &yy_current_buffer->yy_ch_buf[0]; + + return ret_val; + } + + +/* yy_get_previous_state - get the state just before the EOB char was reached */ + +static yy_state_type yy_get_previous_state() + { + register yy_state_type yy_current_state; + register char *yy_cp; + + yy_current_state = yy_start; + + for ( yy_cp = yytext_ptr + YY_MORE_ADJ; yy_cp < yy_c_buf_p; ++yy_cp ) + { + register YY_CHAR yy_c = (*yy_cp ? yy_ec[YY_SC_TO_UI(*yy_cp)] : 1); + if ( yy_accept[yy_current_state] ) + { + yy_last_accepting_state = yy_current_state; + yy_last_accepting_cpos = yy_cp; + } + while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) + { + yy_current_state = (int) yy_def[yy_current_state]; + if ( yy_current_state >= 79 ) + yy_c = yy_meta[(unsigned int) yy_c]; + } + yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c]; + } + + return yy_current_state; + } + + +/* yy_try_NUL_trans - try to make a transition on the NUL character + * + * synopsis + * next_state = yy_try_NUL_trans( current_state ); + */ + +#ifdef YY_USE_PROTOS +static yy_state_type yy_try_NUL_trans( yy_state_type yy_current_state ) +#else +static yy_state_type yy_try_NUL_trans( yy_current_state ) +yy_state_type yy_current_state; +#endif + { + register int yy_is_jam; + register char *yy_cp = yy_c_buf_p; + + register YY_CHAR yy_c = 1; + if ( yy_accept[yy_current_state] ) + { + yy_last_accepting_state = yy_current_state; + yy_last_accepting_cpos = yy_cp; + } + while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) + { + yy_current_state = (int) yy_def[yy_current_state]; + if ( yy_current_state >= 79 ) + yy_c = yy_meta[(unsigned int) yy_c]; + } + yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c]; + yy_is_jam = (yy_current_state == 78); + + return yy_is_jam ? 0 : yy_current_state; + } + + +#ifndef YY_NO_UNPUT +#ifdef YY_USE_PROTOS +static void yyunput( int c, register char *yy_bp ) +#else +static void yyunput( c, yy_bp ) +int c; +register char *yy_bp; +#endif + { + register char *yy_cp = yy_c_buf_p; + + /* undo effects of setting up yytext */ + *yy_cp = yy_hold_char; + + if ( yy_cp < yy_current_buffer->yy_ch_buf + 2 ) + { /* need to shift things up to make room */ + /* +2 for EOB chars. */ + register int number_to_move = yy_n_chars + 2; + register char *dest = &yy_current_buffer->yy_ch_buf[ + yy_current_buffer->yy_buf_size + 2]; + register char *source = + &yy_current_buffer->yy_ch_buf[number_to_move]; + + while ( source > yy_current_buffer->yy_ch_buf ) + *--dest = *--source; + + yy_cp += (int) (dest - source); + yy_bp += (int) (dest - source); + yy_current_buffer->yy_n_chars = + yy_n_chars = yy_current_buffer->yy_buf_size; + + if ( yy_cp < yy_current_buffer->yy_ch_buf + 2 ) + YY_FATAL_ERROR( "flex scanner push-back overflow" ); + } + + *--yy_cp = (char) c; + + + yytext_ptr = yy_bp; + yy_hold_char = *yy_cp; + yy_c_buf_p = yy_cp; + } +#endif /* ifndef YY_NO_UNPUT */ + + +#ifdef __cplusplus +static int yyinput() +#else +static int input() +#endif + { + int c; + + *yy_c_buf_p = yy_hold_char; + + if ( *yy_c_buf_p == YY_END_OF_BUFFER_CHAR ) + { + /* yy_c_buf_p now points to the character we want to return. + * If this occurs *before* the EOB characters, then it's a + * valid NUL; if not, then we've hit the end of the buffer. + */ + if ( yy_c_buf_p < &yy_current_buffer->yy_ch_buf[yy_n_chars] ) + /* This was really a NUL. */ + *yy_c_buf_p = '\0'; + + else + { /* need more input */ + int offset = yy_c_buf_p - yytext_ptr; + ++yy_c_buf_p; + + switch ( yy_get_next_buffer() ) + { + case EOB_ACT_LAST_MATCH: + /* This happens because yy_g_n_b() + * sees that we've accumulated a + * token and flags that we need to + * try matching the token before + * proceeding. But for input(), + * there's no matching to consider. + * So convert the EOB_ACT_LAST_MATCH + * to EOB_ACT_END_OF_FILE. + */ + + /* Reset buffer status. */ + yyrestart( yyin ); + + /* fall through */ + + case EOB_ACT_END_OF_FILE: + { + if ( yywrap() ) + return EOF; + + if ( ! yy_did_buffer_switch_on_eof ) + YY_NEW_FILE; +#ifdef __cplusplus + return yyinput(); +#else + return input(); +#endif + } + + case EOB_ACT_CONTINUE_SCAN: + yy_c_buf_p = yytext_ptr + offset; + break; + } + } + } + + c = *(unsigned char *) yy_c_buf_p; /* cast for 8-bit char's */ + *yy_c_buf_p = '\0'; /* preserve yytext */ + yy_hold_char = *++yy_c_buf_p; + + + return c; + } + + +#ifdef YY_USE_PROTOS +void yyrestart( FILE *input_file ) +#else +void yyrestart( input_file ) +FILE *input_file; +#endif + { + if ( ! yy_current_buffer ) + yy_current_buffer = yy_create_buffer( yyin, YY_BUF_SIZE ); + + yy_init_buffer( yy_current_buffer, input_file ); + yy_load_buffer_state(); + } + + +#ifdef YY_USE_PROTOS +void yy_switch_to_buffer( YY_BUFFER_STATE new_buffer ) +#else +void yy_switch_to_buffer( new_buffer ) +YY_BUFFER_STATE new_buffer; +#endif + { + if ( yy_current_buffer == new_buffer ) + return; + + if ( yy_current_buffer ) + { + /* Flush out information for old buffer. */ + *yy_c_buf_p = yy_hold_char; + yy_current_buffer->yy_buf_pos = yy_c_buf_p; + yy_current_buffer->yy_n_chars = yy_n_chars; + } + + yy_current_buffer = new_buffer; + yy_load_buffer_state(); + + /* We don't actually know whether we did this switch during + * EOF (yywrap()) processing, but the only time this flag + * is looked at is after yywrap() is called, so it's safe + * to go ahead and always set it. + */ + yy_did_buffer_switch_on_eof = 1; + } + + +#ifdef YY_USE_PROTOS +void yy_load_buffer_state( void ) +#else +void yy_load_buffer_state() +#endif + { + yy_n_chars = yy_current_buffer->yy_n_chars; + yytext_ptr = yy_c_buf_p = yy_current_buffer->yy_buf_pos; + yyin = yy_current_buffer->yy_input_file; + yy_hold_char = *yy_c_buf_p; + } + + +#ifdef YY_USE_PROTOS +YY_BUFFER_STATE yy_create_buffer( FILE *file, int size ) +#else +YY_BUFFER_STATE yy_create_buffer( file, size ) +FILE *file; +int size; +#endif + { + YY_BUFFER_STATE b; + + b = (YY_BUFFER_STATE) yy_flex_alloc( sizeof( struct yy_buffer_state ) ); + if ( ! b ) + YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" ); + + b->yy_buf_size = size; + + /* yy_ch_buf has to be 2 characters longer than the size given because + * we need to put in 2 end-of-buffer characters. + */ + b->yy_ch_buf = (char *) yy_flex_alloc( b->yy_buf_size + 2 ); + if ( ! b->yy_ch_buf ) + YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" ); + + b->yy_is_our_buffer = 1; + + yy_init_buffer( b, file ); + + return b; + } + + +#ifdef YY_USE_PROTOS +void yy_delete_buffer( YY_BUFFER_STATE b ) +#else +void yy_delete_buffer( b ) +YY_BUFFER_STATE b; +#endif + { + if ( ! b ) + return; + + if ( b == yy_current_buffer ) + yy_current_buffer = (YY_BUFFER_STATE) 0; + + if ( b->yy_is_our_buffer ) + yy_flex_free( (void *) b->yy_ch_buf ); + + yy_flex_free( (void *) b ); + } + + +#ifndef YY_ALWAYS_INTERACTIVE +#ifndef YY_NEVER_INTERACTIVE +extern int isatty YY_PROTO(( int )); +#endif +#endif + +#ifdef YY_USE_PROTOS +void yy_init_buffer( YY_BUFFER_STATE b, FILE *file ) +#else +void yy_init_buffer( b, file ) +YY_BUFFER_STATE b; +FILE *file; +#endif + + + { + yy_flush_buffer( b ); + + b->yy_input_file = file; + b->yy_fill_buffer = 1; + +#if YY_ALWAYS_INTERACTIVE + b->yy_is_interactive = 1; +#else +#if YY_NEVER_INTERACTIVE + b->yy_is_interactive = 0; +#else + b->yy_is_interactive = file ? (isatty( fileno(file) ) > 0) : 0; +#endif +#endif + } + + +#ifdef YY_USE_PROTOS +void yy_flush_buffer( YY_BUFFER_STATE b ) +#else +void yy_flush_buffer( b ) +YY_BUFFER_STATE b; +#endif + + { + if ( ! b ) + return; + + b->yy_n_chars = 0; + + /* We always need two end-of-buffer characters. The first causes + * a transition to the end-of-buffer state. The second causes + * a jam in that state. + */ + b->yy_ch_buf[0] = YY_END_OF_BUFFER_CHAR; + b->yy_ch_buf[1] = YY_END_OF_BUFFER_CHAR; + + b->yy_buf_pos = &b->yy_ch_buf[0]; + + b->yy_at_bol = 1; + b->yy_buffer_status = YY_BUFFER_NEW; + + if ( b == yy_current_buffer ) + yy_load_buffer_state(); + } + + +#ifndef YY_NO_SCAN_BUFFER +#ifdef YY_USE_PROTOS +YY_BUFFER_STATE yy_scan_buffer( char *base, yy_size_t size ) +#else +YY_BUFFER_STATE yy_scan_buffer( base, size ) +char *base; +yy_size_t size; +#endif + { + YY_BUFFER_STATE b; + + if ( size < 2 || + base[size-2] != YY_END_OF_BUFFER_CHAR || + base[size-1] != YY_END_OF_BUFFER_CHAR ) + /* They forgot to leave room for the EOB's. */ + return 0; + + b = (YY_BUFFER_STATE) yy_flex_alloc( sizeof( struct yy_buffer_state ) ); + if ( ! b ) + YY_FATAL_ERROR( "out of dynamic memory in yy_scan_buffer()" ); + + b->yy_buf_size = size - 2; /* "- 2" to take care of EOB's */ + b->yy_buf_pos = b->yy_ch_buf = base; + b->yy_is_our_buffer = 0; + b->yy_input_file = 0; + b->yy_n_chars = b->yy_buf_size; + b->yy_is_interactive = 0; + b->yy_at_bol = 1; + b->yy_fill_buffer = 0; + b->yy_buffer_status = YY_BUFFER_NEW; + + yy_switch_to_buffer( b ); + + return b; + } +#endif + + +#ifndef YY_NO_SCAN_STRING +#ifdef YY_USE_PROTOS +YY_BUFFER_STATE yy_scan_string( yyconst char *yy_str ) +#else +YY_BUFFER_STATE yy_scan_string( yy_str ) +yyconst char *yy_str; +#endif + { + int len; + for ( len = 0; yy_str[len]; ++len ) + ; + + return yy_scan_bytes( yy_str, len ); + } +#endif + + +#ifndef YY_NO_SCAN_BYTES +#ifdef YY_USE_PROTOS +YY_BUFFER_STATE yy_scan_bytes( yyconst char *bytes, int len ) +#else +YY_BUFFER_STATE yy_scan_bytes( bytes, len ) +yyconst char *bytes; +int len; +#endif + { + YY_BUFFER_STATE b; + char *buf; + yy_size_t n; + int i; + + /* Get memory for full buffer, including space for trailing EOB's. */ + n = len + 2; + buf = (char *) yy_flex_alloc( n ); + if ( ! buf ) + YY_FATAL_ERROR( "out of dynamic memory in yy_scan_bytes()" ); + + for ( i = 0; i < len; ++i ) + buf[i] = bytes[i]; + + buf[len] = buf[len+1] = YY_END_OF_BUFFER_CHAR; + + b = yy_scan_buffer( buf, n ); + if ( ! b ) + YY_FATAL_ERROR( "bad buffer in yy_scan_bytes()" ); + + /* It's okay to grow etc. this buffer, and we should throw it + * away when we're done. + */ + b->yy_is_our_buffer = 1; + + return b; + } +#endif + + +#ifndef YY_NO_PUSH_STATE +#ifdef YY_USE_PROTOS +static void yy_push_state( int new_state ) +#else +static void yy_push_state( new_state ) +int new_state; +#endif + { + if ( yy_start_stack_ptr >= yy_start_stack_depth ) + { + yy_size_t new_size; + + yy_start_stack_depth += YY_START_STACK_INCR; + new_size = yy_start_stack_depth * sizeof( int ); + + if ( ! yy_start_stack ) + yy_start_stack = (int *) yy_flex_alloc( new_size ); + + else + yy_start_stack = (int *) yy_flex_realloc( + (void *) yy_start_stack, new_size ); + + if ( ! yy_start_stack ) + YY_FATAL_ERROR( + "out of memory expanding start-condition stack" ); + } + + yy_start_stack[yy_start_stack_ptr++] = YY_START; + + BEGIN(new_state); + } +#endif + + +#ifndef YY_NO_POP_STATE +static void yy_pop_state() + { + if ( --yy_start_stack_ptr < 0 ) + YY_FATAL_ERROR( "start-condition stack underflow" ); + + BEGIN(yy_start_stack[yy_start_stack_ptr]); + } +#endif + + +#ifndef YY_NO_TOP_STATE +static int yy_top_state() + { + return yy_start_stack[yy_start_stack_ptr - 1]; + } +#endif + +#ifndef YY_EXIT_FAILURE +#define YY_EXIT_FAILURE 2 +#endif + +#ifdef YY_USE_PROTOS +static void yy_fatal_error( yyconst char msg[] ) +#else +static void yy_fatal_error( msg ) +char msg[]; +#endif + { + (void) fprintf( stderr, "%s\n", msg ); + exit( YY_EXIT_FAILURE ); + } + + + +/* Redefine yyless() so it works in section 3 code. */ + +#undef yyless +#define yyless(n) \ + do \ + { \ + /* Undo effects of setting up yytext. */ \ + yytext[yyleng] = yy_hold_char; \ + yy_c_buf_p = yytext + n; \ + yy_hold_char = *yy_c_buf_p; \ + *yy_c_buf_p = '\0'; \ + yyleng = n; \ + } \ + while ( 0 ) + + +/* Internal utility routines. */ + +#ifndef yytext_ptr +#ifdef YY_USE_PROTOS +static void yy_flex_strncpy( char *s1, yyconst char *s2, int n ) +#else +static void yy_flex_strncpy( s1, s2, n ) +char *s1; +yyconst char *s2; +int n; +#endif + { + register int i; + for ( i = 0; i < n; ++i ) + s1[i] = s2[i]; + } +#endif + +#ifdef YY_NEED_STRLEN +#ifdef YY_USE_PROTOS +static int yy_flex_strlen( yyconst char *s ) +#else +static int yy_flex_strlen( s ) +yyconst char *s; +#endif + { + register int n; + for ( n = 0; s[n]; ++n ) + ; + + return n; + } +#endif + + +#ifdef YY_USE_PROTOS +static void *yy_flex_alloc( yy_size_t size ) +#else +static void *yy_flex_alloc( size ) +yy_size_t size; +#endif + { + return (void *) malloc( size ); + } + +#ifdef YY_USE_PROTOS +static void *yy_flex_realloc( void *ptr, yy_size_t size ) +#else +static void *yy_flex_realloc( ptr, size ) +void *ptr; +yy_size_t size; +#endif + { + /* The cast to (char *) in the following accommodates both + * implementations that use char* generic pointers, and those + * that use void* generic pointers. It works with the latter + * because both ANSI C and C++ allow castless assignment from + * any pointer type to void*, and deal with argument conversions + * as though doing an assignment. + */ + return (void *) realloc( (char *) ptr, size ); + } + +#ifdef YY_USE_PROTOS +static void yy_flex_free( void *ptr ) +#else +static void yy_flex_free( ptr ) +void *ptr; +#endif + { + free( ptr ); + } + +#if YY_MAIN +int main() + { + yylex(); + return 0; + } +#endif +#line 70 "hsm.l" + + + + +int yywrap() +{ + return 1; +} + +void yyerror(char* s) +{ + printf("\n%d: %s at %s\n", lineno, s, yytext); +} + + + + diff --git a/source/hsm-parser.tab.c b/source/hsm-parser.tab.c new file mode 100644 index 0000000..3abe7fe --- /dev/null +++ b/source/hsm-parser.tab.c @@ -0,0 +1,469 @@ +#ifndef lint +static char yysccsid[] = "@(#)yaccpar 1.9 (Berkeley) 02/21/93"; +#endif +#define YYBYACC 1 +#define YYMAJOR 1 +#define YYMINOR 9 +#define yyclearin (yychar=(-1)) +#define yyerrok (yyerrflag=0) +#define YYRECOVERING (yyerrflag!=0) +#define YYPREFIX "yy" +#line 2 "hsm.y" + + +extern int yylex(); +extern void yyerror(char* s); + +#include "main.h" + +#line 11 "hsm.y" +typedef union +{ + char string[258]; + float constant; +} YYSTYPE; +#line 26 "hsm-parser.tab.c" +#define MACHINE 257 +#define STATE 258 +#define DEFAULT 259 +#define ENTRY 260 +#define IDLE 261 +#define EXIT 262 +#define TRANSITION 263 +#define ACTION 264 +#define TERMINATE 265 +#define IDENTIFIER 266 +#define CONSTANT 267 +#define YYERRCODE 256 +short yylhs[] = { -1, + 0, 0, 1, 3, 3, 4, 4, 5, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 2, 7, 8, + 9, 10, 11, 12, 13, 14, 15, 16, +}; +short yylen[] = { 2, + 1, 2, 4, 1, 2, 1, 1, 4, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 6, 4, 4, + 4, 4, 4, 6, 6, 6, 6, 4, +}; +short yydefred[] = { 0, + 0, 0, 1, 0, 0, 2, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 4, 6, 7, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, + 5, 0, 9, 10, 11, 12, 13, 14, 15, 16, + 17, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 18, 19, 20, 21, 22, 23, 0, + 0, 0, 0, 28, 8, 0, 0, 0, 0, 24, + 26, 25, 27, +}; +short yydgoto[] = { 2, + 3, 4, 17, 18, 19, 20, 21, 22, 23, 24, + 25, 26, 27, 28, 29, 30, +}; +short yysindex[] = { -251, + -33, -251, 0, -106, -248, 0, -249, -25, -20, -19, + -18, -17, -15, -14, -13, -12, -125, 0, 0, 0, + -99, -30, -29, -28, -27, -26, -24, -23, -22, -21, + -233, -227, -226, -225, -224, -223, -264, -262, -222, 0, + 0, -249, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 4, 5, 7, 8, 9, 10, 11, 12, 13, + 14, 18, -117, 0, 0, 0, 0, 0, 0, -214, + -213, -212, -206, 0, 0, 20, 21, 22, 23, 0, + 0, 0, 0, +}; +short yyrindex[] = { 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, +}; +short yygindex[] = { 0, + 63, 0, 24, -16, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, +}; +#define YYTABLESIZE 148 +short yytable[] = { 40, + 41, 58, 59, 60, 61, 1, 5, 75, 9, 10, + 11, 12, 13, 14, 15, 16, 7, 8, 31, 32, + 33, 34, 35, 42, 36, 37, 38, 39, 43, 44, + 45, 46, 47, 52, 48, 49, 50, 51, 53, 54, + 55, 56, 57, 62, 64, 65, 41, 66, 67, 68, + 69, 76, 77, 78, 70, 71, 72, 73, 74, 79, + 80, 81, 82, 83, 6, 63, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 9, 10, 11, 12, 13, 14, 15, 16, + 9, 10, 11, 12, 13, 14, 15, 16, +}; +short yycheck[] = { 125, + 17, 266, 267, 266, 267, 257, 40, 125, 258, 259, + 260, 261, 262, 263, 264, 265, 123, 266, 44, 40, + 40, 40, 40, 123, 40, 40, 40, 40, 59, 59, + 59, 59, 59, 267, 59, 59, 59, 59, 266, 266, + 266, 266, 266, 266, 41, 41, 63, 41, 41, 41, + 41, 266, 266, 266, 44, 44, 44, 44, 41, 266, + 41, 41, 41, 41, 2, 42, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 258, 259, 260, 261, 262, 263, 264, 265, + 258, 259, 260, 261, 262, 263, 264, 265, +}; +#define YYFINAL 2 +#ifndef YYDEBUG +#define YYDEBUG 0 +#endif +#define YYMAXTOKEN 267 +#if YYDEBUG +char *yyname[] = { +"end-of-file",0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,"'('","')'",0,0,"','",0,0,0,0,0,0,0,0,0,0,0,0,0,0,"';'",0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,"'{'",0,"'}'",0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,"MACHINE", +"STATE","DEFAULT","ENTRY","IDLE","EXIT","TRANSITION","ACTION","TERMINATE", +"IDENTIFIER","CONSTANT", +}; +char *yyrule[] = { +"$accept : machines", +"machines : machine", +"machines : machines machine", +"machine : machine_decl '{' state_items '}'", +"state_items : state_item", +"state_items : state_items state_item", +"state_item : state", +"state_item : statement", +"state : state_decl '{' state_items '}'", +"statement : default ';'", +"statement : entry ';'", +"statement : idle ';'", +"statement : exit ';'", +"statement : transition ';'", +"statement : action ';'", +"statement : timetransition ';'", +"statement : timeaction ';'", +"statement : terminate ';'", +"machine_decl : MACHINE '(' IDENTIFIER ',' CONSTANT ')'", +"state_decl : STATE '(' IDENTIFIER ')'", +"default : DEFAULT '(' IDENTIFIER ')'", +"entry : ENTRY '(' IDENTIFIER ')'", +"idle : IDLE '(' IDENTIFIER ')'", +"exit : EXIT '(' IDENTIFIER ')'", +"transition : TRANSITION '(' IDENTIFIER ',' IDENTIFIER ')'", +"action : ACTION '(' IDENTIFIER ',' IDENTIFIER ')'", +"timetransition : TRANSITION '(' CONSTANT ',' IDENTIFIER ')'", +"timeaction : ACTION '(' CONSTANT ',' IDENTIFIER ')'", +"terminate : TERMINATE '(' IDENTIFIER ')'", +}; +#endif +#ifdef YYSTACKSIZE +#undef YYMAXDEPTH +#define YYMAXDEPTH YYSTACKSIZE +#else +#ifdef YYMAXDEPTH +#define YYSTACKSIZE YYMAXDEPTH +#else +#define YYSTACKSIZE 500 +#define YYMAXDEPTH 500 +#endif +#endif +int yydebug; +int yynerrs; +int yyerrflag; +int yychar; +short *yyssp; +YYSTYPE *yyvsp; +YYSTYPE yyval; +YYSTYPE yylval; +short yyss[YYSTACKSIZE]; +YYSTYPE yyvs[YYSTACKSIZE]; +#define yystacksize YYSTACKSIZE +#define YYABORT goto yyabort +#define YYREJECT goto yyabort +#define YYACCEPT goto yyaccept +#define YYERROR goto yyerrlab +int +yyparse() +{ + register int yym, yyn, yystate; +#if YYDEBUG + register char *yys; + extern char *getenv(); + + if (yys = getenv("YYDEBUG")) + { + yyn = *yys; + if (yyn >= '0' && yyn <= '9') + yydebug = yyn - '0'; + } +#endif + + yynerrs = 0; + yyerrflag = 0; + yychar = (-1); + + yyssp = yyss; + yyvsp = yyvs; + *yyssp = yystate = 0; + +yyloop: + if (yyn = yydefred[yystate]) goto yyreduce; + if (yychar < 0) + { + if ((yychar = yylex()) < 0) yychar = 0; +#if YYDEBUG + if (yydebug) + { + yys = 0; + if (yychar <= YYMAXTOKEN) yys = yyname[yychar]; + if (!yys) yys = "illegal-symbol"; + printf("%sdebug: state %d, reading %d (%s)\n", + YYPREFIX, yystate, yychar, yys); + } +#endif + } + if ((yyn = yysindex[yystate]) && (yyn += yychar) >= 0 && + yyn <= YYTABLESIZE && yycheck[yyn] == yychar) + { +#if YYDEBUG + if (yydebug) + printf("%sdebug: state %d, shifting to state %d\n", + YYPREFIX, yystate, yytable[yyn]); +#endif + if (yyssp >= yyss + yystacksize - 1) + { + goto yyoverflow; + } + *++yyssp = yystate = yytable[yyn]; + *++yyvsp = yylval; + yychar = (-1); + if (yyerrflag > 0) --yyerrflag; + goto yyloop; + } + if ((yyn = yyrindex[yystate]) && (yyn += yychar) >= 0 && + yyn <= YYTABLESIZE && yycheck[yyn] == yychar) + { + yyn = yytable[yyn]; + goto yyreduce; + } + if (yyerrflag) goto yyinrecovery; +#ifdef lint + goto yynewerror; +#endif +yynewerror: + yyerror("syntax error"); +#ifdef lint + goto yyerrlab; +#endif +yyerrlab: + ++yynerrs; +yyinrecovery: + if (yyerrflag < 3) + { + yyerrflag = 3; + for (;;) + { + if ((yyn = yysindex[*yyssp]) && (yyn += YYERRCODE) >= 0 && + yyn <= YYTABLESIZE && yycheck[yyn] == YYERRCODE) + { +#if YYDEBUG + if (yydebug) + printf("%sdebug: state %d, error recovery shifting\ + to state %d\n", YYPREFIX, *yyssp, yytable[yyn]); +#endif + if (yyssp >= yyss + yystacksize - 1) + { + goto yyoverflow; + } + *++yyssp = yystate = yytable[yyn]; + *++yyvsp = yylval; + goto yyloop; + } + else + { +#if YYDEBUG + if (yydebug) + printf("%sdebug: error recovery discarding state %d\n", + YYPREFIX, *yyssp); +#endif + if (yyssp <= yyss) goto yyabort; + --yyssp; + --yyvsp; + } + } + } + else + { + if (yychar == 0) goto yyabort; +#if YYDEBUG + if (yydebug) + { + yys = 0; + if (yychar <= YYMAXTOKEN) yys = yyname[yychar]; + if (!yys) yys = "illegal-symbol"; + printf("%sdebug: state %d, error recovery discards token %d (%s)\n", + YYPREFIX, yystate, yychar, yys); + } +#endif + yychar = (-1); + goto yyloop; + } +yyreduce: +#if YYDEBUG + if (yydebug) + printf("%sdebug: state %d, reducing by rule %d (%s)\n", + YYPREFIX, yystate, yyn, yyrule[yyn]); +#endif + yym = yylen[yyn]; + yyval = yyvsp[1-yym]; + switch (yyn) + { +case 3: +#line 37 "hsm.y" +{ + parseEndMachine(); + } +break; +case 8: +#line 51 "hsm.y" +{ + parseEndState(); + } +break; +case 18: +#line 68 "hsm.y" +{ + parseBeginMachine(yyvsp[-3].string,(int)yyvsp[-1].constant); + } +break; +case 19: +#line 74 "hsm.y" +{ + parseBeginState(yyvsp[-1].string); + } +break; +case 20: +#line 80 "hsm.y" +{ + parseDefault(yyvsp[-1].string); + } +break; +case 21: +#line 86 "hsm.y" +{ + parseEntry(yyvsp[-1].string); + } +break; +case 22: +#line 92 "hsm.y" +{ + parseIdle(yyvsp[-1].string); + } +break; +case 23: +#line 98 "hsm.y" +{ + parseExit(yyvsp[-1].string); + } +break; +case 24: +#line 104 "hsm.y" +{ + parseTransition(yyvsp[-3].string,yyvsp[-1].string); + } +break; +case 25: +#line 110 "hsm.y" +{ + parseAction(yyvsp[-3].string,yyvsp[-1].string); + } +break; +case 26: +#line 116 "hsm.y" +{ + parseTimeTransition(yyvsp[-3].constant,yyvsp[-1].string); + } +break; +case 27: +#line 122 "hsm.y" +{ + parseTimeAction(yyvsp[-3].constant,yyvsp[-1].string); + } +break; +case 28: +#line 128 "hsm.y" +{ + parseTerminate(yyvsp[-1].string); + } +break; +#line 414 "hsm-parser.tab.c" + } + yyssp -= yym; + yystate = *yyssp; + yyvsp -= yym; + yym = yylhs[yyn]; + if (yystate == 0 && yym == 0) + { +#if YYDEBUG + if (yydebug) + printf("%sdebug: after reduction, shifting from state 0 to\ + state %d\n", YYPREFIX, YYFINAL); +#endif + yystate = YYFINAL; + *++yyssp = YYFINAL; + *++yyvsp = yyval; + if (yychar < 0) + { + if ((yychar = yylex()) < 0) yychar = 0; +#if YYDEBUG + if (yydebug) + { + yys = 0; + if (yychar <= YYMAXTOKEN) yys = yyname[yychar]; + if (!yys) yys = "illegal-symbol"; + printf("%sdebug: state %d, reading %d (%s)\n", + YYPREFIX, YYFINAL, yychar, yys); + } +#endif + } + if (yychar == 0) goto yyaccept; + goto yyloop; + } + if ((yyn = yygindex[yym]) && (yyn += yystate) >= 0 && + yyn <= YYTABLESIZE && yycheck[yyn] == yystate) + yystate = yytable[yyn]; + else + yystate = yydgoto[yym]; +#if YYDEBUG + if (yydebug) + printf("%sdebug: after reduction, shifting from state %d \ +to state %d\n", YYPREFIX, *yyssp, yystate); +#endif + if (yyssp >= yyss + yystacksize - 1) + { + goto yyoverflow; + } + *++yyssp = yystate; + *++yyvsp = yyval; + goto yyloop; +yyoverflow: + yyerror("yacc stack overflow"); +yyabort: + return (1); +yyaccept: + return (0); +} diff --git a/source/hsm-parser.tab.h b/source/hsm-parser.tab.h new file mode 100644 index 0000000..c95c397 --- /dev/null +++ b/source/hsm-parser.tab.h @@ -0,0 +1,17 @@ +#define MACHINE 257 +#define STATE 258 +#define DEFAULT 259 +#define ENTRY 260 +#define IDLE 261 +#define EXIT 262 +#define TRANSITION 263 +#define ACTION 264 +#define TERMINATE 265 +#define IDENTIFIER 266 +#define CONSTANT 267 +typedef union +{ + char string[258]; + float constant; +} YYSTYPE; +extern YYSTYPE yylval; diff --git a/source/hsm.l b/source/hsm.l new file mode 100644 index 0000000..6ea1c4e --- /dev/null +++ b/source/hsm.l @@ -0,0 +1,86 @@ +%{ + + +#include +#include +#include + +#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); +} + + + + diff --git a/source/hsm.y b/source/hsm.y new file mode 100644 index 0000000..9dd5300 --- /dev/null +++ b/source/hsm.y @@ -0,0 +1,135 @@ +%{ + + +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 IDENTIFIER +%token 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); + } + ; + + + + diff --git a/source/hsmc.dev b/source/hsmc.dev new file mode 100644 index 0000000..b99642e --- /dev/null +++ b/source/hsmc.dev @@ -0,0 +1,159 @@ +[Project] +FileName=hsmc.dev +Name=hsmc +UnitCount=11 +Type=1 +Ver=1 +ObjFiles= +Includes= +Libs= +PrivateResource= +ResourceIncludes= +MakeIncludes= +Compiler= +CppCompiler= +Linker= +IsCpp=1 +Icon= +ExeOutput= +ObjectOutput= +OverrideOutput=0 +OverrideOutputName=hsmc.exe +HostApplication= +Folders= +CommandLine=../example/parsetest/parsetest.hsm +IncludeVersionInfo=0 +SupportXPThemes=0 +CompilerSet=0 +CompilerSettings=0000000000000001000 +UseCustomMakefile=0 +CustomMakefile= + +[Unit1] +FileName=autofile.h +CompileCpp=1 +Folder=hsmc +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit2] +FileName=config.h +CompileCpp=1 +Folder=hsmc +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit3] +FileName=getopt.cpp +CompileCpp=1 +Folder=hsmc +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit4] +FileName=getopt.h +CompileCpp=1 +Folder=hsmc +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit5] +FileName=hsm-lexer.c +CompileCpp=1 +Folder=hsmc +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit6] +FileName=hsm-parser.tab.c +CompileCpp=1 +Folder=hsmc +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit7] +FileName=hsm-parser.tab.h +CompileCpp=1 +Folder=hsmc +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit8] +FileName=machine.h +CompileCpp=1 +Folder=hsmc +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit9] +FileName=main.cpp +CompileCpp=1 +Folder=hsmc +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit10] +FileName=main.h +CompileCpp=1 +Folder=hsmc +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit11] +FileName=state.h +CompileCpp=1 +Folder=hsmc +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[VersionInfo] +Major=0 +Minor=1 +Release=1 +Build=1 +LanguageID=1033 +CharsetID=1252 +CompanyName= +FileVersion= +FileDescription=Developed using the Dev-C++ IDE +InternalName= +LegalCopyright= +LegalTrademarks= +OriginalFilename= +ProductName= +ProductVersion= +AutoIncBuildNr=0 + diff --git a/source/machine.h b/source/machine.h new file mode 100644 index 0000000..9b4fd81 --- /dev/null +++ b/source/machine.h @@ -0,0 +1,488 @@ +#ifndef __machine_h__ +#define __machine_h__ + + +#include +#include +#include +#include + +#include "state.h" + +class Machine +{ +public: + + // data + std::string name; + + std::vector states; + std::map statemap; + + std::set event_refs; + std::set state_refs; + std::set action_refs; + + // construction + Machine(); + + // add data + int AddState(std::string name); + int EndState(); + bool AddDefault(std::string state); + bool AddEntry(std::string action); + bool AddIdle(std::string action); + bool AddExit(std::string action); + bool AddTransition(char* event,char* state); + bool AddAction(char* event,char* action); + bool AddTimeTransition(float time,char* state); + bool AddTimeAction(float time,char* action); + bool AddTerminate(char* event); + + // get data + std::string CurStateName(); + int GetMaxDepth(); + int GetTimerDepth(); + + // finalize machine + bool EndMachine(); + + // settings + void SetQueueSize(int size); + int GetQueueSize(); + +private: + + int curstate; + int maxdepth; + int depth; + int timerdepth; + int queuesize; + + int CountTimers(int sid); + + bool Validate(); +}; + + +// initialize a machine +Machine::Machine() +{ + // set current state to invalid + curstate = -1; + + // default depth + maxdepth = 0; + depth = 0; +} + +int Machine::AddState(std::string name) +{ + // make sure this isn't a duplicate state + if ( statemap.find(name) != statemap.end() ) + { + return -1; + } + + // figure out our new state id + int id = (int)states.size(); + + // add this state to our name -> id map + statemap[name] = id; + + // tell parent about this new child state + if ( curstate >= 0 ) + { + states[curstate].child.push_back(id); + } + + // create new state + State s; + s.name = name; + + // tell current state about parent + s.parent = curstate; + + // add state to list and make current + states.push_back(s); + + // record depth + depth++; + if ( depth > maxdepth ) + { + maxdepth = depth; + } + + // make this new state the current state + curstate = id; + return curstate; +} + +int Machine::EndState() +{ + // dec depth + depth--; + + // move curstate to parent + if ( curstate >= 0 ) + { + curstate = states[curstate].parent; + } + return curstate; +} + +bool Machine::AddDefault(std::string state) +{ + if ( curstate < 0 ) + { + return false; + } + else if ( states[curstate].defaultstate != "" ) + { + // default state already set for this state + return false; + } + else + { + states[curstate].defaultstate = state; + state_refs.insert(state); + return true; + } +} + +bool Machine::AddEntry(std::string action) +{ + if ( curstate < 0 ) + { + return false; + } + + // add entry action and reference + states[curstate].entry.push_back(action); + action_refs.insert(action); + + return true; +} + +bool Machine::AddIdle(std::string action) +{ + if ( curstate < 0 ) + { + return false; + } + + // add idle action and reference + states[curstate].idle.push_back(action); + action_refs.insert(action); + + return true; +} + +bool Machine::AddExit(std::string action) +{ + if ( curstate < 0 ) + { + return false; + } + + // add exit action and reference + states[curstate].exit.push_back(action); + action_refs.insert(action); + + return true; +} + +bool Machine::AddTransition(char* event,char* state) +{ + if ( curstate < 0 ) + { + return false; + } + + // make certain a transition doesn't already exist for this event + if ( states[curstate].transition.find(event) != states[curstate].transition.end() ) + { + // transition already exists for this event + return false; + } + + // add transition + states[curstate].transition[event] = state; + + // add references + event_refs.insert(event); + state_refs.insert(state); + + return true; +} + +bool Machine::AddAction(char* event,char* action) +{ + if ( curstate < 0 ) + { + return false; + } + + // add action + states[curstate].action.insert( std::pair(event,action) ); + + // add references + event_refs.insert(event); + action_refs.insert(action); + + return true; +} + +bool Machine::AddTimeTransition(float time,char* state) +{ + if ( curstate < 0 ) + { + return false; + } + + // add timeout + states[curstate].timetransition.insert( std::pair(time,state) ); + + // add references + state_refs.insert(state); + + return true; +} + +bool Machine::AddTimeAction(float time,char* action) +{ + if ( curstate < 0 ) + { + return false; + } + + // add timeout + states[curstate].timeaction.insert( std::pair(time,action) ); + + // add references + action_refs.insert(action); + + return true; +} + +bool Machine::AddTerminate(char* event) +{ + if ( curstate < 0 ) + { + return false; + } + else if ( states[curstate].terminate.find(event) != states[curstate].terminate.end() ) + { + // already exists + return false; + } + + // add terminate + states[curstate].terminate.insert(event); + + // add reference + event_refs.insert(event); + + return true; +} + +std::string Machine::CurStateName() +{ + if ( curstate < 0 ) + { + return ""; + } + else + { + return states[curstate].name; + } +} + +int Machine::GetMaxDepth() +{ + return maxdepth; +} + +int Machine::GetTimerDepth() +{ + return timerdepth; +} + +bool Machine::EndMachine() +{ + // now let's validate it + return Validate(); +} + +int Machine::CountTimers(int sid) +{ + int count = 0; + while ( sid >= 0 ) + { + count += (int)states[sid].timers.size(); + sid = states[sid].parent; + } + + return count; +} + + +bool Machine::Validate() +{ + bool ok = true; + + // make sure all referenced states are defined + for ( std::set::iterator it = state_refs.begin(); it != state_refs.end(); it++ ) + { + if ( statemap.find( *it ) == statemap.end() ) + { + ok = false; + printf( + "Error: State '%s' referenced but not defined in Machine '%s'\n", + (*it).c_str(),name.c_str()); + } + } + + // make sure events listed as terminate are not also listed in actions or transitions + for ( std::vector::iterator it = states.begin(); it != states.end(); it++ ) + { + // for convenience + State& s = (*it); + + // check each "terminate event and make sure it's not in the action or transition list + for ( std::set::iterator term = s.terminate.begin(); term != s.terminate.end(); term++ ) + { + // look in action & transitions lists + if ( s.action.find(*term) != s.action.end() || s.transition.find(*term) != s.transition.end() ) + { + ok = false; + printf( + "Error: Terminate event '%s' is not unique in State '%s', Machine '%s'\n", + (*term).c_str(), s.name.c_str(), name.c_str() + ); + } + } + } + + // make sure all of the state names are NOT event names + for ( std::vector::iterator it = states.begin(); it != states.end(); it++ ) + { + State& s = (*it); + + // look for this name in the event list + if ( event_refs.find(s.name) != event_refs.end() ) + { + ok = false; + printf( + "Error: State-Event name collision on '%s' in Machine '%s'\n", + s.name.c_str(), + name.c_str() + ); + } + } + + // make certain that no timed actions or transitions exist after shortest timed + // transition in each state + for ( std::vector::iterator it = states.begin(); it != states.end(); it++ ) + { + State& s = (*it); + + // if no timed transitions we're done + if ( s.timetransition.size() == 0 ) + { + continue; + } + + // find shortest timed transition + std::multimap::iterator ttit = s.timetransition.begin(); + float shorttime = (*ttit).first; + std::string shorttrans = (*ttit).second; + + // now iterate timed transitions and warn about remaining + for ( ttit++; ttit != s.timetransition.end(); ttit++ ) + { + printf( + "Error: Timed Transition '%s/%f' superceded by '%s/%f' in State '%s', Machine '%s'\n", + (*ttit).second.c_str(), + (*ttit).first, + shorttrans.c_str(), + shorttime, + s.name.c_str(), + name.c_str() + ); + s.timetransition.erase(ttit); + ok = false; + } + + // iterate through timed actions and warn about anything over + std::multimap::iterator tait; + for ( tait = s.timeaction.begin(); tait != s.timeaction.end(); tait++ ) + { + if ( (*tait).first >= shorttime ) + { + printf( + "Error: Timed Action '%s/%f' superceded by '%s/%f' in State '%s', Machine '%s'\n", + (*tait).second.c_str(), + (*tait).first, + shorttrans.c_str(), + shorttime, + s.name.c_str(), + name.c_str() + ); + s.timeaction.erase(tait); + ok = false; + } + } + } + + // now assign timers (value+id) for each state + for ( std::vector::iterator it = states.begin(); it != states.end(); it++ ) + { + State& s = (*it); + + // now count up timer values for this state + std::set timervalues; + for ( std::multimap::iterator ttit = s.timetransition.begin(); ttit != s.timetransition.end(); ttit++ ) + { + timervalues.insert( (*ttit).first ); + } + for ( std::multimap::iterator tait = s.timeaction.begin(); tait != s.timeaction.end(); tait++ ) + { + timervalues.insert( (*tait).first ); + } + + // assign timer id's to values + int ptimers = CountTimers(s.parent); + for ( std::set::iterator myit = timervalues.begin(); myit != timervalues.end(); myit++ ) + { + s.timers[ptimers++] = *myit; + } + } + + // determine max timer depth + timerdepth = 0; + for ( unsigned int i=0;i timerdepth ) + { + timerdepth = depth; + } + } + + return ok; +} + +void Machine::SetQueueSize(int size) +{ + queuesize = size; +} + +int Machine::GetQueueSize() +{ + return queuesize; +} + +#endif // __machine_h__ + + diff --git a/source/main.cpp b/source/main.cpp new file mode 100644 index 0000000..4561cea --- /dev/null +++ b/source/main.cpp @@ -0,0 +1,1387 @@ +#include +#include + +#include +#include +#include +#include + +#include "getopt.h" +#include "main.h" +#include "autofile.h" +#include "machine.h" +#include "state.h" +#include "config.h" + + +// +// globals +// + +std::vector gMachines; +int gCurMachine; +Config gConfig; +int gErrors = 0; + + + +// +// parser interface functions +// + +extern "C" FILE* yyin; + +extern "C" void parseEndMachine() +{ + if ( !gMachines[gCurMachine].EndMachine() ) + { + gErrors++; + } +} + +extern "C" void parseEndState() +{ + gMachines[gCurMachine].EndState(); +} + +extern "C" void parseBeginMachine(char* name, int eventsize) +{ + // validate event size + if ( eventsize <= 0 ) + { + extern int lineno; + printf("%d: Invalid event queue size for Machine '%s'\n", + lineno, + name); + gErrors++; + } + + // create new machine + Machine m; + m.name = name; + m.SetQueueSize(eventsize); + + // create top state + m.AddState("TOPSTATE"); + + // add machine to list + gMachines.push_back(m); + + // update current machine id + gCurMachine = (int)gMachines.size() - 1; +} + +extern "C" void parseBeginState(char* name) +{ + if ( gMachines[gCurMachine].AddState(name) < 0 ) + { + extern int lineno; + printf("%d: State '%s' was previously defined\n", + lineno, + name); + gErrors++; + } +} + +extern "C" void parseDefault(char* state) +{ + if ( !gMachines[gCurMachine].AddDefault(state) ) + { + extern int lineno; + printf("%d: multiple Default states defined in State '%s'\n", + lineno, + (gMachines[gCurMachine].CurStateName()).c_str()); + gErrors++; + } +} + +extern "C" void parseEntry(char* action) +{ + gMachines[gCurMachine].AddEntry(action); +} + +extern "C" void parseIdle(char* action) +{ + gMachines[gCurMachine].AddIdle(action); +} + +extern "C" void parseExit(char* action) +{ + gMachines[gCurMachine].AddExit(action); +} + +extern "C" void parseTransition(char* event,char* state) +{ + if ( !gMachines[gCurMachine].AddTransition(event,state) ) + { + extern int lineno; + printf("%d: multiple Transition's on '%s' defined in '%s'\n", + lineno, + event, + (gMachines[gCurMachine].CurStateName()).c_str()); + gErrors++; + } +} + +extern "C" void parseAction(char* event,char* action) +{ + gMachines[gCurMachine].AddAction(event,action); +} + +extern "C" void parseTimeTransition(float time,char* state) +{ + gMachines[gCurMachine].AddTimeTransition(time,state); +} + +extern "C" void parseTimeAction(float time,char* action) +{ + gMachines[gCurMachine].AddTimeAction(time,action); +} + +extern "C" void parseTerminate(char* event) +{ + if ( !gMachines[gCurMachine].AddTerminate(event) ) + { + extern int lineno; + printf("%d: multiple Terminate's on '%s' defined in '%s'\n", + lineno, + event, + (gMachines[gCurMachine].CurStateName()).c_str()); + gErrors++; + } +} + + + +// +// output functions +// + +void OutputEvents(FILE* f, Machine& m) +{ + fprintf(f, + "\tenum\n" + "\t{\n" + ); + bool first = true; + for ( std::set::iterator it = m.event_refs.begin(); it != m.event_refs.end(); it++ ) + { + fprintf(f,"\t\t%s%s,\n",(*it).c_str(), first?" = 0":""); + first = false; + } + fprintf(f, + "\n" + "\t\t___HSM_NUM_EVENTS___\n" + "\t};\n\n"); +} + +void OutputStateIDs(FILE* f, Machine& m) +{ + fprintf(f, + "\tenum\n" + "\t{\n" + ); + bool first = true; + for ( std::vector::iterator it = m.states.begin(); it != m.states.end(); it++ ) + { + fprintf(f,"\t\t%s%s,\n",(*it).name.c_str(), first?" = 0":""); + first = false; + } + fprintf(f, + "\n" + "\t\t___HSM_NUM_STATES___\n" + "\t};\n\n"); +} + +void OutputPrivateEvents(FILE* f, Machine& m) +{ + // header + fprintf(f, + "\tenum\n" + "\t{\n" + "\t\t___HSM_ENTRY___ = ___HSM_NUM_EVENTS___,\n" + "\t\t___HSM_IDLE___,\n" + "\t\t___HSM_EXIT___,\n" + "\t\t___HSM_ELICIT_INITIAL_TRANSITION___,\n" + "\t\t___HSM_ELICIT_PARENT___,\n" + ); + + // private timer events + for ( int i=0;i::iterator it = m.action_refs.begin(); it != m.action_refs.end(); it++ ) + { + fprintf(f,"\tvirtual void %s()%s;\n",(*it).c_str(),gConfig.PureVirtuals ? " = 0" : ""); + } + fprintf(f,"\n"); +} + +void OutputActions(FILE* f, Machine& m) +{ + fprintf(f, + "//\n" + "// actions (must be overridden)\n" + "//\n" + ); + + for ( std::set::iterator it = m.action_refs.begin(); it != m.action_refs.end(); it++ ) + { + fprintf(f,"void %s::%s()\n",m.name.c_str(),(*it).c_str()); + fprintf(f, + "{\n" + "\t// empty\n" + "}\n\n" + ); + } +} + +void OutputStateProtos(FILE* f, Machine& m, int s) +{ + static int indent = 0; + + fprintf(f,"\tint %*s___HSMSTATE_%s___(int event);\n",indent*4,"",m.states[s].name.c_str()); + + indent++; + for ( unsigned int i=0;i::iterator it = m.states.begin(); it != m.states.end(); it++ ) + { + State& s = (*it); + std::vector null_events; + + // header + fprintf(f, + "//\n" + "// STATE: %s::%s\n" + "//\n" + "int %s::___HSMSTATE_%s___(int event)\n" + "{\n" + "\tswitch (event)\n" + "\t{\n", + m.name.c_str(),s.name.c_str(), + m.name.c_str(),s.name.c_str()); + + fprintf(f, + "\t\t// internal events\n\n" + ); + + // entry + fprintf(f, + "\t\tcase ___HSM_ENTRY___:\n" + ); + if ( gConfig.Debug ) + { + fprintf(f, + "\t\t\t___HSM_DEBUG___(\"Entering %s\");\n", + s.name.c_str() + ); + } + for ( std::map::iterator timeit = s.timers.begin(); timeit != s.timers.end(); timeit++ ) + { + fprintf(f, + "\t\t\t___HSM_START_TIMER___(%d,%0.7ff);\n", + (*timeit).first, + (*timeit).second + ); + } + for ( unsigned int i=0;i 0 ) + { + fprintf(f, + "\t\tcase ___HSM_IDLE___:\n" + ); + for ( unsigned int i=0;i::iterator timeit = s.timers.begin(); timeit != s.timers.end(); timeit++ ) + { + fprintf(f, + "\t\t\t___HSM_STOP_TIMER___(%d);\n", + (*timeit).first + ); + } + for ( unsigned int i=0;i 0 ) + { + fprintf(f, + "\t\t// timer events\n\n" + ); + for ( std::map::iterator timeit = s.timers.begin(); timeit != s.timers.end(); timeit++ ) + { + // which timer + fprintf(f, + "\t\tcase ___HSM_TIMER_%d___:\n", + (*timeit).first + ); + + // all actions with this time + for ( std::multimap::iterator acit = s.timeaction.begin(); acit != s.timeaction.end(); acit++ ) + { + if ( (*acit).first == (*timeit).second ) + { + // print debug info on action + if ( gConfig.Debug ) + { + fprintf(f, + "\t\t\t___HSM_DEBUG___(\"[TIMER EVENT]: %s()\");\n", + (*acit).second.c_str() + ); + } + + // call timer action + fprintf(f, + "\t\t\t%s();\n", + (*acit).second.c_str() + ); + } + } + + // any transition with this time? + for ( std::multimap::iterator trit = s.timetransition.begin(); trit != s.timetransition.end(); trit++ ) + { + if ( (*trit).first == (*timeit).second ) + { + // print debug info on transition + if ( gConfig.Debug ) + { + fprintf(f, + "\t\t\t___HSM_DEBUG___(\"[TIMER EVENT]: %s -> %s\");\n", + s.name.c_str(), + (*trit).second.c_str() + ); + } + + // do the transition + fprintf(f, + "\t\t\t___HSM_TRANSITION___(%s);\n", + (*trit).second.c_str() + ); + break; // only ONE transition allowed; should never happen, but be safe + } + } + + // return event handled + fprintf(f, + "\t\t\treturn ___HSM_NULL_STATE___;\n" + "\n" + ); + } + } + + // user events + if ( s.terminate.size() > 0 || s.transition.size() > 0 || s.action.size() > 0 ) + { + fprintf(f, + "\t\t// user events\n\n" + ); + } + + // terminate's if they exist + for ( std::set::iterator it = s.terminate.begin(); it != s.terminate.end(); it++ ) + { + fprintf(f, + "\t\tcase %s:\n", + (*it).c_str() + ); + } + if ( s.terminate.size() > 0 ) + { + // print debug info on terminal event + if ( gConfig.Debug ) + { + fprintf(f, + "\t\t\t___HSM_DEBUG___(\"[TERMINAL EVENT]: %s -> [HSM TERMINATION]\");\n", + s.name.c_str() + ); + } + + // terminate + fprintf(f, + "\t\t\t___HSM_STOP_MACHINE___();\n" + "\t\t\treturn ___HSM_TERMINAL_STATE___;\n" + "\n" + ); + } + + // transition's if they exist + for ( std::map::iterator it = s.transition.begin(); it != s.transition.end(); it++ ) + { + fprintf(f, + "\t\tcase %s:\n", + (*it).first.c_str() + ); + + // see if this event is used in any actions + for ( std::multimap::iterator tit = s.action.lower_bound((*it).first); tit != s.action.upper_bound((*it).first); tit++ ) + { + // print debug info on action + if ( gConfig.Debug ) + { + fprintf(f, + "\t\t\t___HSM_DEBUG___(\"%s: %s()\");\n", + (*it).first.c_str(), + (*tit).second.c_str() + ); + } + + // call action + fprintf(f, + "\t\t\t%s();\n", + (*tit).second.c_str() + ); + + } + + // print debug info on transition + if ( gConfig.Debug ) + { + fprintf(f, + "\t\t\t___HSM_DEBUG___(\"%s: %s -> %s\");\n", + (*it).first.c_str(), + s.name.c_str(), + (*it).second.c_str() + ); + } + + // now the transition + fprintf(f, + "\t\t\t___HSM_TRANSITION___(%s);\n" + "\t\t\treturn ___HSM_NULL_STATE___;\n\n", + (*it).second.c_str() + ); + } + + // actions if they exist + for ( std::multimap::iterator it = s.action.begin(); it != s.action.end(); it++ ) + { + // store off for ease of typing + std::string event = (*it).first; + + // did this action have a transition too? + if ( s.transition.find(event) == s.transition.end() ) + { + // how many actions associated with this event? + int count = (int)s.action.count(event); + + // print event + fprintf(f, + "\t\tcase %s:\n", + event.c_str() + ); + + for ( int i=0;i= 0 ? m.states[s.parent].name.c_str() : "___HSM_NULL_STATE___" + ); + + // footer + fprintf(f, + "\t}\n" + "}\n" + "\n" + ); + } +} + +void OutputStateTable(FILE* f, Machine& m) +{ + fprintf(f, + "%s::___HSMSTATE_FUNC___ %s::___HSM_STATE_TAB___[___HSM_NUM_STATES___] = \n" + "{\n", + m.name.c_str(),m.name.c_str() + ); + + for ( unsigned int i=0;i\n" + "\tclass StaticQueue\n" + "\t{\n" + "\tpublic:\n" + "\n" + "\t\tStaticQueue()\n" + "\t\t{\n" + "\t\t\tclear();\n" + "\t\t}\n" + "\n" + "\t\tvoid clear()\n" + "\t\t{\n" + "\t\t\tfirst = 0;\n" + "\t\t\tlast = -1;\n" + "\n" + "\t\t\tcursize = 0;\n" + "\t\t}\n" + "\n" + "\t\tbool put(T x)\n" + "\t\t{\n" + "\t\t\tif ( size() < N )\n" + "\t\t\t{\n" + "\t\t\t\tincrement(last);\n" + "\t\t\t\tdata[last] = x;\n" + "\t\t\t\tcursize++;\n" + "\t\t\t\treturn true;\n" + "\t\t\t}\n" + "\t\t\telse\n" + "\t\t\t{\n" + "\t\t\t\treturn false;\n" + "\t\t\t}\n" + "\t\t}\n" + "\n" + "\t\tbool get(T& x)\n" + "\t\t{\n" + "\t\t\tif ( size() > 0 )\n" + "\t\t\t{\n" + "\t\t\t\tx = data[first];\n" + "\t\t\t\tincrement(first);\n" + "\t\t\t\tcursize--;\n" + "\t\t\t\treturn true;\n" + "\t\t\t}\n" + "\t\t\telse\n" + "\t\t\t{\n" + "\t\t\t\treturn false;\n" + "\t\t\t}\n" + "\t\t}\n" + "\n" + "\t\tunsigned int size() const\n" + "\t\t{\n" + "\t\t\treturn cursize;\n" + "\t\t}\n" + "\n" + "\t\tbool IsEmpty() const\n" + "\t\t{\n" + "\t\t\treturn size() == 0;\n" + "\t\t}\n" + "\n" + "\t\tbool IsFull() const\n" + "\t\t{\n" + "\t\t\treturn size() == N;\n" + "\t\t}\n" + "\n" + "\tprivate:\n" + "\n" + "\t\tT data[N];\n" + "\t\tint first,last,cursize;\n" + "\n" + "\t\tvoid increment(int& x)\n" + "\t\t{\n" + "\t\t\tif ( ++x >= N )\n" + "\t\t\t{\n" + "\t\t\t\tx = 0;\n" + "\t\t\t}\n" + "\t\t}\n" + "\t};\n" + "\n" + ); + + // event queue instance + fprintf(f, + "\t// event queue\n" + "\tStaticQueue ___HSM_EVENT_QUEUE___;\n" + "\n", + m.GetQueueSize() + ); + + // debug string pointer + fprintf(f, + "\t// debug string pointer\n" + "\tchar* ___HSM_DEBUG_MSG___;\n" + "\n" + ); + + // our data + fprintf(f, + "\t// current state\n" + "\tint ___HSM_CURSTATE___;\n" + "\n" + ); + + // private events + fprintf(f, + "\t// private events\n" + ); + OutputPrivateEvents(f,m); + + // a few other #defines + fprintf(f, + "\t// special private constants\n" + "\tenum\n" + "\t{\n" + "\t\t___HSM_NULL_STATE___ = -1,\n" + "\t\t___HSM_TERMINAL_STATE___ = -2,\n" + "\t\t___HSM_ERROR___ = -3,\n" + "\t\t___HSM_MAX_DEPTH___ = %d,\n" + "\t\t___HSM_TIMER_DEPTH___ = %d\n" + "\t};\n" + "\n", + m.GetMaxDepth(), + m.GetTimerDepth() + ); + + // timer declaration + if ( m.GetTimerDepth() > 0 ) + { + fprintf(f, + "\t// timers\n" + "\tfloat ___HSM_TIMERS___[___HSM_TIMER_DEPTH___];\n" + "\n" + ); + } + + // state prototypes + fprintf(f, + "\t// state declaration\n" + ); + OutputStateProtos(f,m,0); // <-- recursive + fprintf(f,"\n"); + + // state table declaration + fprintf(f, + "\t// state table declaration\n" + "\ttypedef int (%s::*___HSMSTATE_FUNC___)(int);\n" + "\tstatic ___HSMSTATE_FUNC___ ___HSM_STATE_TAB___[___HSM_NUM_STATES___];\n" + "\n", + m.name.c_str() + ); + + // machine driver prototypes + fprintf(f, + "\t// machine driver prototypes\n" + "\tvoid ___HSM_START_MACHINE___();\n" + "\tvoid ___HSM_STOP_MACHINE___();\n" + "\tvoid ___HSM_CALC_HIERARCHY___(int state, int path[___HSM_MAX_DEPTH___]);\n" + "\tvoid ___HSM_TRANSITION___(int state);\n" + "\tint ___HSM_CALL_STATE___(int state, int event);\n" + "\tvoid ___HSM_HANDLE_EVENT___(int event);\n" + "\tvoid ___HSM_UPDATE_TIMERS___(float dt);\n" + "\tvoid ___HSM_START_TIMER___(int id, float time);\n" + "\tvoid ___HSM_STOP_TIMER___(int id);\n" + "\tvoid ___HSM_DEBUG___(char* msg);\n" + "\n" + ); + + // footer + fprintf(f, + "};\n" + "\n" + "#endif // ___HSMC_GENERATED_%s___\n" + "\n", + m.name.c_str() + ); + +} + +void OutputMachineHSMDebug(FILE* f, Machine& m) +{ + fprintf(f, + "//\n" + "// debug hook (override this to trap debug messages)\n" + "//\n" + ); + + fprintf(f, + "void %s::HSMDebug(char* msg)\n" + "{\n" + "\t// empty\n" + "}\n" + "\n", + m.name.c_str() + ); +} + +void OutputMachineDriverCode(FILE* f, Machine& m) +{ + fprintf(f, + "%s::%s()\n" + "{\n" + "\t___HSM_CURSTATE___ = ___HSM_NULL_STATE___;\n" + "\t___HSM_DEBUG_MSG___ = \"\";\n" + "}\n" + "\n" + "%s::~%s()\n" + "{\n" + "\tif ( ___HSM_CURSTATE___ != ___HSM_NULL_STATE___ )\n" + "\t{\n" + "\t\t#if _DEBUG\n" + "\t\t\tprintf(\"\\n\\nHSM SHOULD BE EXITED via HSMDestruct() BEFORE DESTRUCTION!!!\\n\\n\\n\");\n" + "\t\t\twhile(1); // infinite loop\n" + "\t\t#else // _DEBUG\n" + "\t\t\tHSMDestruct();\n" + "\t\t#endif // _DEBUG\n" + "\t}\n" + "}\n" + "\n" + "int %s::HSMGetCurrentState() const\n" + "{\n" + "\treturn ___HSM_CURSTATE___;\n" + "}\n" + "\n" + "int %s::HSMGetParentState(int state) const\n" + "{\n" + "\t// valid state?\n" + "\tif ( state >= 0 && state < ___HSM_NUM_STATES___ )\n" + "\t{\n" + "\t\t// const_cast used here because we know that pinging a state with ___HSM_ELICIT_PARENT___ is const-safe\n" + "\t\treturn const_cast<%s*>(this)->___HSM_CALL_STATE___(state,___HSM_ELICIT_PARENT___);\n" + "\t}\n" + "\n" + "\treturn ___HSM_NULL_STATE___;\n" + "}\n" + "\n" + "bool %s::HSMIsInState(int state) const\n" + "{\n" + "\tint parent = ___HSM_CURSTATE___;\n" + "\n" + "\t// valid state?\n" + "\tif ( state >= 0 && state < ___HSM_NUM_STATES___ )\n" + "\t{\n" + "\t\tdo\n" + "\t\t{\n" + "\t\t\tif ( state == parent )\n" + "\t\t\t{\n" + "\t\t\t\treturn true;\n" + "\t\t\t}\n" + "\t\t}\n" + "\t\twhile ( (parent = HSMGetParentState(parent)) >= 0 );\n" + "\t}\n" + "\n" + "\treturn false;\n" + "}\n" + "\n" + "bool %s::HSMIsRunning() const\n" + "{\n" + "\tint s = HSMGetCurrentState();\n" + "\treturn (s >= 0 && s < ___HSM_NUM_STATES___);\n" + "}\n" + "\n" + "void %s::HSMConstruct()\n" + "{\n" + "\t___HSM_START_MACHINE___();\n" + "}\n" + "\n" + "void %s::HSMDestruct()\n" + "{\n" + "\t___HSM_STOP_MACHINE___();\n" + "}\n" + "\n" + "bool %s::HSMUpdate(float dt)\n" + "{\n" + "\t\t// TODO - update timers, trigger events as necessary\n" + "\tif ( HSMIsRunning() )\n" + "\t{\n" + "\t\t// update timers\n" + "\t\t___HSM_UPDATE_TIMERS___(dt);\n" + "\n" + "\t\t// process all current events\n" + "\t\tunsigned int numevents = ___HSM_EVENT_QUEUE___.size();\n" + "\t\tfor (unsigned int i=0;i 0;i++)\n" + "\t\t{\n" + "\t\t\tint e = -1;\n" + "\t\t\tif ( ___HSM_EVENT_QUEUE___.get(e) )\n" + "\t\t\t{\n" + "\t\t\t\t___HSM_HANDLE_EVENT___(e);\n" + "\t\t\t}\n" + "\t\t}\n" + "\n" + "\t\t// now handle our idle event (for the call to Update())\n" + "\t\t___HSM_HANDLE_EVENT___(___HSM_IDLE___);\n" + "\t}\n" + "\n" + "\t// return if we're still running\n" + "\treturn HSMIsRunning();\n" + "}\n" + "\n" + "void %s::HSMTrigger(int event)\n" + "{\n" + "\tif ( event >= 0 && event < ___HSM_NUM_EVENTS___ )\n" + "\t{\n" + "\t\tif ( !___HSM_EVENT_QUEUE___.put(event) )\n" + "\t\t{\n" + "\t\t\t___HSM_DEBUG___(\"Event queue overflow!!!\");\n" + "\t\t}\n" + "\t}\n" + "}\n" + "\n" + "char* %s::HSMGetLastDebugMessage()\n" + "{\n" + "\treturn ___HSM_DEBUG_MSG___;\n" + "}\n" + "\n" + "void %s::___HSM_HANDLE_EVENT___(int event)\n" + "{\n" + "\tif ( event >= 0 && event < ___HSM_TOTAL_NUM_EVENTS___ )\n" + "\t{\n" + "\t\tint s = ___HSM_CURSTATE___;\n" + "\t\twhile ( s >= 0 && s < ___HSM_NUM_STATES___ )\n" + "\t\t{\n" + "\t\t\ts = ___HSM_CALL_STATE___(s,event);\n" + "\t\t}\n" + "\t}\n" + "}\n" + "\n" + "void %s::___HSM_START_MACHINE___()\n" + "{\n" + "\tif ( ___HSM_CURSTATE___ == ___HSM_NULL_STATE___ )\n" + "\t{\n" + "\t\t// empty event queue\n" + "\t\t___HSM_EVENT_QUEUE___.clear();\n" + "\n" + "\t\t// reset any timers\n" + "\t\tfor (int i=0;i<___HSM_TIMER_DEPTH___;i++)\n" + "\t\t{\n" + "\t\t\t___HSM_STOP_TIMER___(i);\n" + "\t\t}\n" + "\n" + "\t\t// go to first state\n" + "\t\t___HSM_TRANSITION___(TOPSTATE);\n" + "\t}\n" + "}\n" + "\n" + "void %s::___HSM_STOP_MACHINE___()\n" + "{\n" + "\tif ( ___HSM_CURSTATE___ != ___HSM_NULL_STATE___ )\n" + "\t{\n" + "\t\t// exit all states to the root\n" + "\t\twhile ( ___HSM_CURSTATE___ != TOPSTATE )\n" + "\t\t{\n" + "\t\t\t___HSM_CALL_STATE___(___HSM_CURSTATE___,___HSM_EXIT___);\n" + "\t\t\t___HSM_CURSTATE___ = ___HSM_CALL_STATE___(___HSM_CURSTATE___,___HSM_ELICIT_PARENT___);\n" + "\t\t}\n" + "\n" + "\t\t// and finally exit the root state\n" + "\t\t___HSM_CALL_STATE___(TOPSTATE,___HSM_EXIT___);\n" + "\t\t___HSM_CURSTATE___ = ___HSM_NULL_STATE___;\n" + "\t}\n" + "}\n" + "\n" + "int %s::___HSM_CALL_STATE___(int state, int event)\n" + "{\n" + "\tif ( state < 0 || state >= ___HSM_NUM_STATES___ )\n" + "\t{\n" + "\t\t// invalid state id\n" + "\t\treturn ___HSM_ERROR___;\n" + "\t}\n" + "\telse\n" + "\t{\n" + "\t\t// valid state, lookup state function in table and call it\n" + "\t\treturn ((*this).*(___HSM_STATE_TAB___[state])) (event);\n" + "\t}\n" + "}\n" + "\n" + "void %s::___HSM_CALC_HIERARCHY___(int state, int path[___HSM_MAX_DEPTH___])\n" + "{\n" + "\tint num = 0;\n" + "\tint parent;\n" + "\n" + "\t// valid state?\n" + "\tif ( state >= 0 && state < ___HSM_NUM_STATES___ )\n" + "\t{\n" + "\t\t// add self\n" + "\t\tpath[num++] = state;\n" + "\n" + "\t\t// add parents\n" + "\t\tparent = state;\n" + "\t\twhile ( (parent = ___HSM_CALL_STATE___(parent,___HSM_ELICIT_PARENT___)) >= 0 )\n" + "\t\t{\n" + "\t\t\tpath[num++] = parent;\n" + "\t\t}\n" + "\t}\n" + "\n" + "\t// set remainder to NULL\n" + "\tfor (int i=num;i<___HSM_MAX_DEPTH___;i++)\n" + "\t{\n" + "\t\t// note that an invalid state comes straight here\n" + "\t\tpath[i] = ___HSM_NULL_STATE___;\n" + "\t}\n" + "}\n" + "\n" + "void %s::___HSM_TRANSITION___(int state)\n" + "{\n" + "\twhile ( state >= 0 && state < ___HSM_NUM_STATES___ )\n" + "\t{\n" + "\t\t// determine path to root for each state\n" + "\t\tint ExitPath[___HSM_MAX_DEPTH___];\n" + "\t\tint EntryPath[___HSM_MAX_DEPTH___];\n" + "\t\t___HSM_CALC_HIERARCHY___(___HSM_CURSTATE___,ExitPath);\n" + "\t\t___HSM_CALC_HIERARCHY___(state,EntryPath);\n" + "\n" + "\t\t//\n" + "\t\t// discover LCA\n" + "\t\t//\n" + "\t\tint ientry = ___HSM_MAX_DEPTH___ - 1;\n" + "\t\tint iexit = ___HSM_MAX_DEPTH___ - 1;\n" + "\n" + "\t\t// find root state in each path (or -1)\n" + "\t\twhile ( ientry >= 0 && EntryPath[ientry] == ___HSM_NULL_STATE___ )\n" + "\t\t{\n" + "\t\t\tientry--;\n" + "\t\t}\n" + "\t\twhile ( iexit >= 0 && ExitPath[iexit] == ___HSM_NULL_STATE___ )\n" + "\t\t{\n" + "\t\t\tiexit--;\n" + "\t\t}\n" + "\n" + "\t\t// beginning at root states, walk backwards until we find the LCA\n" + "\t\twhile ( ientry >= 0 && iexit >= 0 && EntryPath[ientry] == ExitPath[iexit] )\n" + "\t\t{\n" + "\t\t\tientry--;\n" + "\t\t\tiexit--;\n" + "\t\t}\n" + "\n" + "\t\t// deal with self transition\n" + "\t\tif ( ___HSM_CURSTATE___ == state && ientry == -1 && iexit == -1 )\n" + "\t\t{\n" + "\t\t\tientry++;\n" + "\t\t\tiexit++;\n" + "\t\t}\n" + "\n" + "\t\t//\n" + "\t\t// walk transition\n" + "\t\t//\n" + "\n" + "\t\t// call exit routines\n" + "\t\tfor ( int i=0;i<=iexit;i++ )\n" + "\t\t{\n" + "\t\t\t___HSM_CALL_STATE___(ExitPath[i],___HSM_EXIT___);\n" + "\t\t}\n" + "\n" + "\t\t// call entry routines\n" + "\t\tfor ( int i=ientry;i>=0;i-- )\n" + "\t\t{\n" + "\t\t\t___HSM_CALL_STATE___(EntryPath[i],___HSM_ENTRY___);\n" + "\t\t}\n" + "\n" + "\t\t// set current state\n" + "\t\t___HSM_CURSTATE___ = state;\n" + "\n" + "\t\t// now check state for Default transitions\n" + "\t\tstate = ___HSM_CALL_STATE___(___HSM_CURSTATE___,___HSM_ELICIT_INITIAL_TRANSITION___);\n" + "\t}\n" + "}\n" + "\n" + "void %s::___HSM_DEBUG___(char* msg)\n" + "{\n" + "\t___HSM_DEBUG_MSG___ = msg;\n" + "\tHSMDebug(msg);\n" + "}\n" + "\n", + m.name.c_str(), + m.name.c_str(), + m.name.c_str(), + m.name.c_str(), + m.name.c_str(), + m.name.c_str(), + m.name.c_str(), + m.name.c_str(), + m.name.c_str(), + m.name.c_str(), + m.name.c_str(), + m.name.c_str(), + m.name.c_str(), + m.name.c_str(), + m.name.c_str(), + m.name.c_str(), + m.name.c_str(), + m.name.c_str(), + m.name.c_str(), + m.name.c_str(), + m.name.c_str() + ); + + // timer functions + if ( m.GetTimerDepth() > 0 ) + { + fprintf(f, + "void %s::___HSM_UPDATE_TIMERS___(float dt)\n" + "{\n" + "\tfor (int i=0;i<___HSM_TIMER_DEPTH___;i++)\n" + "\t{\n" + "\t\tif ( ___HSM_TIMERS___[i] >= 0.0f )\n" + "\t\t{\n" + "\t\t\t___HSM_TIMERS___[i] -= dt;\n" + "\t\t\tif ( ___HSM_TIMERS___[i] < 0.0f )\n" + "\t\t\t{\n" + "\t\t\t\tif ( !___HSM_EVENT_QUEUE___.put(___HSM_TIMER_0___+i) )\n" + "\t\t\t\t{\n" + "\t\t\t\t\t___HSM_DEBUG___(\"Event queue overflow!!!\");\n" + "\t\t\t\t}\n" + "\t\t\t}\n" + "\t\t}\n" + "\t}\n" + "}\n" + "\n" + "void %s::___HSM_START_TIMER___(int id, float time)\n" + "{\n" + "\t___HSM_TIMERS___[id] = time;\n" + "}\n" + "\n" + "void %s::___HSM_STOP_TIMER___(int id)\n" + "{\n" + "\t___HSM_TIMERS___[id] = -1.0f;\n" + "}\n" + "\n", + m.name.c_str(), + m.name.c_str(), + m.name.c_str() + ); + } + else + { + fprintf(f, + "void %s::___HSM_UPDATE_TIMERS___(float dt)\n" + "{ // no timers in this machine\n" + "}\n" + "\n" + "void %s::___HSM_START_TIMER___(int id, float time)\n" + "{ // no timers in this machine\n" + "}\n" + "\n" + "void %s::___HSM_STOP_TIMER___(int id)\n" + "{ // no timers in this machine\n" + "}\n" + "\n", + m.name.c_str(), + m.name.c_str(), + m.name.c_str() + ); + } +} + +void OutputMachineCPP(FILE* f, std::string incfile, Machine& m){ + // a warning + OutputNotice(f); + + fprintf(f, + "#include \"%s\"\n" + "#if _DEBUG\n" + "#\tinclude \n" + "#endif // _DEBUG\n" + "\n", + incfile.c_str() + ); + + // actions + OutputActions(f,m); + + // HSMDebug() overrideable + OutputMachineHSMDebug(f,m); + + // state table + OutputStateTable(f,m); + + // driver code + OutputMachineDriverCode(f,m); + + // now states + OutputStates(f,m); +} + + + +// +// main program +// + +int main(int argc, char *argv[]) +{ + // parse command line + if ( !gConfig.ParseCommandLine(argc,argv) ) + { + return 1; + } + + // header + if ( !gConfig.Quiet ) + { + printf("Compiling '%s'...\n",gConfig.InFileName.c_str()); + } + + // open input file + AutoFILE infile((char*)gConfig.InFileName.c_str(),"r"); + if ( !infile.Opened() ) + { + printf("ERROR: unable to open '%s'\n",gConfig.InFileName.c_str()); + return 1; + } + + // parse input file + yyin = infile; + if ( yyparse() != 0 ) + { + printf("ERROR: parser failed\n"); + return 1; + } + + // bail if we had errors during parse or validation [done at EndMachine()] + if ( gErrors > 0 ) + { + printf("\nERROR: compilation aborted due to errors\n"); + return 1; + } + + // input looks OK! + if ( !gConfig.Quiet ) + { + printf("Writing '%s', '%s'...\n",gConfig.OutCPPFileName.c_str(),gConfig.OutHFileName.c_str()); + } + + // open output files + AutoFILE cppout((char*)gConfig.OutCPPFileName.c_str(),"w"); + if ( !cppout.Opened() ) + { + printf("ERROR: unable to open '%s'\n",gConfig.OutCPPFileName.c_str()); + return 1; + } + AutoFILE hout((char*)gConfig.OutHFileName.c_str(),"w"); + if ( !hout.Opened() ) + { + printf("ERROR: unable to open '%s'\n",gConfig.OutHFileName.c_str()); + return 1; + } + + // output code + for ( std::vector::iterator it = gMachines.begin(); it != gMachines.end(); it++ ) + { + OutputMachineH( hout, *it ); + OutputMachineCPP( cppout, gConfig.IncludeName, *it ); + } + + // done + if ( !gConfig.Quiet ) + { + printf("done.\n"); + } + return 0; +} + diff --git a/source/main.h b/source/main.h new file mode 100644 index 0000000..20fd1a4 --- /dev/null +++ b/source/main.h @@ -0,0 +1,29 @@ +#ifndef __main_h__ +#define __main_h__ + +#if defined(__cplusplus) +extern "C" { +#endif // defined(__cplusplus) + + void parseEndMachine(); + void parseEndState(); + void parseBeginMachine(char* name,int eventsize); + void parseBeginState(char* name); + void parseDefault(char* state); + void parseEntry(char* action); + void parseIdle(char* action); + void parseExit(char* action); + void parseTransition(char* event,char* state); + void parseAction(char* event,char* action); + void parseTimeTransition(float time,char* state); + void parseTimeAction(float time,char* action); + void parseTerminate(char* event); + + int yyparse(); + +#if defined(__cplusplus) +} +#endif // defined(__cplusplus) + +#endif // __main_h__ + diff --git a/source/state.h b/source/state.h new file mode 100644 index 0000000..09c4baf --- /dev/null +++ b/source/state.h @@ -0,0 +1,33 @@ +#ifndef __state_h__ +#define __state_h__ + +#include +#include +#include + +class State +{ +public: + + std::string name; + + int parent; + std::vector child; + + std::string defaultstate; + std::vector entry; + std::vector idle; + std::vector exit; + std::map transition; + std::multimap action; + std::set terminate; + std::multimap timetransition; + std::multimap timeaction; + + std::map timers; +}; + + +#endif // __state_h__ + +