FSM generator
Installation
You can download from repository and build and install as usual
Versions
0.6
cpp
generated code with option to move toerror
state- Instead of
languages
, now we speak abouttemplates
0.5
- moved to tera templates
- added dot files generation
0.4
- error redefinied action on cpp (watch example iceberg)
- fix error when running on current directory
- Add full proposed file for hand written .h and .cpp on generated one
0.3
- multiline guards with no status name
- multi guard and multi actions
- '_' on input means any other input
- negative guards
- if no transition, it will generate an error
- updated rust-peg lib and working with macros
- if execption is throwed, we will go to error transition
- better error information
- on parsing show line
- check orphans status
- makefile install, test cpp
0.2
- Actions
- private hpp (hand file)
- Template functions to specialize on transaction change
- Anonymous
namespace
for functions - Add comments support on fsm grammar
TODO
-
Error transition is special
-
Inprove exceptions control
-
web ui for templates
-
read template from file
-
Update idata in order to use ispush with btrees and more
-
Complete the cpp example and update on README.md
-
Check fsm format
- detect duplicated states
- detect duplicated inputs and guards
-
output with signals
-
Add templates
Aim
All computing processes consist of receiving an input and processing it by generating an output. From the simplest to the most complex.
Sometimes, the process depends on the context and it is necessary to manage a state.
Messages and state management, therefore, are two fundamental elements of any software process.
That's why I wrote two external DSLs long time ago for both elements (which I still use in production and are a great help).
In this repository I rewrite one of them, the code generator for a state machine.
At the moment it generates C++ code (my most immediate target in production).
You can have data (fields) on inputs structs, and also on each status
To explain the system, I will use the example in cpp_test/fsm
This example is about writing a system that will handle login requests.
First the server will be asked for a password.
This key will be used to encode the username and password (this one will be passed through a hash function) in the login request.
This encoding will be irreversible (hash function). The server will perform the same operation (starting from the hash of the password) to verify the validity.
If it is OK, it will send a login confirmation.
The diagram would look like this:
A list of transitions could be written as:
// Example of fsm to manage login
// on server side
[init]
rq_key -> w_login / send_key
timer -> init
[w_login]
rq_login -> login / send_login
timer
& timeout -> error
-> w_login
[login]
rq_logout -> logout / send_logout
heartbeat -> login / update_hb
timer
& timeout -> logout
-> login
[logout]
timer -> logout
[error]
_ -> error
And this is the input for this tool to generate code
In fact, even the previous diagram has been generated from this DSL
(it generated a graphviz
dot file)
Elements
States
[init]
...
init, w_login, login ... are the states
Depending on the input and the state (with its values as will be seen later), the system will change to a new state.
Transition
rq_key -> w_login
If we receive an input (in this case rq_key
) we go to next state (w_login
)
Input
The elements received by the status machine.
INPUT
v
rq_key -> w_login
In the example they are rq_key, rq_login, rq_logout, heartbeat and timer.
Guards
Functions that will be called depending on the status and input to decide the way forward.
GUARD
v
rq_login & valid -> login / send_login
You can have more than one guard
rq_login & valid & guard2 -> login / send_login
When you apply different options guards (or combination)...
In the example we have valid, timeout, ontime.
rq_login
& valid -> login / send_login
-> logout / log_err
It could be written with negative guard
rq_login
& !valid -> logout / log_err
-> login / send_login
Final status
Behind the -> arrow is the state we will change to.
FINAL_STATUS
v
rq_key -> w_login
Actions
We can define an action to be performed when executing a transaction.
This will be after the final state and '/'.
ACTION
v
rq_login -> logout / log_err
You can have more than one action
rq_login -> logout / log_err action2
In this example we have send_key, send_login...
Special transition
In all states it is necessary to consider all inputs.
But it is very common that many transitions are the same (generally error cases).
This is marked with the input _
Consider the this example status:
[init]
rq_key -> w_login / send_key
timer -> init
_ -> logout / log_err
_ means... any other input...
Therefore considering all possible inputs in this state
error
status and implicit transitions
error
is a special status
You can move to error
status explicitly.
Any transition no defined, will finis on error status.
You can also put some verifications on transiction funcion, and incase
of fail, you can move to error (even when is not explicitly writted on fsm
)
This is so, because checking the params, is so commont that adding guards for it, would generate a lot of sound
In our example...
[init]
rq_key -> w_login / send_key
timer -> init
There are no transations for rq_login
and rq_logout
. Both are implicit and is
equivalent to...
[init]
rq_key -> w_login / send_key
timer -> init
_ -> error
This is the transition function control (obiosly you can specialize as you wish)
// status change functions
std::variant<TO, st_error_t>
And this is when you explicitly write a transition who finished on error status
st_error_t
Another way to end on error transition. If an exception is thrown while the input is being procesed...
} catch
auto nw_st_info = fromin2error<st_init_t, in_rq_key_t>;
;
return std::make_shared<error>;
Comments
Comments starts at //
and continues to the end of line
Usage
To get help...
> fsm_gen -h
fsm_gen 0.6.1
jleahred
Generate code from a simple fsm file
To check the supported templates --show_templs
USAGE:
fsm_gen [FLAGS] [OPTIONS] [fsm_files]...
FLAGS:
-d, --dot-graphviz Generate graphviz dot file
-h, --help Prints help information
--help-cpp Give me some information about generating cpp files
-s, --show-templs Show supported template generators
-V, --version Prints version information
OPTIONS:
-T, --threads <n_threads> Number of threads to use. 0 means one per core ;-) [default: 0]
-t, --templ <templ> Template to generate code (show available --show-templs) [default: cpp]
ARGS:
<fsm_files>... List of fsm files to be processed
The default template is c++
(and at the moment the only one)
You can run:
And it will generate the c++
You can pass a list of fsm
files
The code will be generated on same directory of original .fsm
file
If your shell supports it, you could run...
C++ code generation
Starting from the example login.fsm
the system will create...
fsm_login_gen.h
fsm_login_gen.cpp
You don't have to modify these files.
You have to write your code on next files...
fsm_login_types.h
fsm_login_private.hpp
These two files will be created if don't exist as a reference
A empty reference code for these two files will be added on _gen
files as comments.
Files dependency:
fsm_login_gen.h
Full code on cpp_test/fsm/fsm_login_gen.h
We are informed on when it was created.
This file is the starting point to use or extend the generated status machine in your program.
// generated automatically 2019-03-22 11:24:40
// do not modify it manually
Headers and namespaces
based on filename
Internal class forward declaration
;
typedef std::shared_ptr<BaseState> SState;
The fsm
class you have to instantiate or extend
// -------------------
// F S M
fsm_login_gen.cpp
Full code on cpp_test/fsm/fsm_login_gen.cpp
You must not modify this file, and it's not necessary to know much about it
fsm_login_types.h
Full code on cpp_test/fsm/fsm_login_types.h
In this file you have to declare the status info types, and input types
If the file doesn't exist, it will be created with empty data types
// Code generated automatically to be filled manually
// This file will not be updated by generator
// It's created just the first time as a reference
// Generator will allways create a .reference file to help with
// new methods and so
On namespace
based on fsm file, we have the two types to declare
You can, of course, complete these types on cpp file if necessary
// namespace login
// FSM_LOGIN_H
fsm_login_private.hpp
Full code on cpp_test/fsm/fsm_login_private.hpp
This is the other file you have to maintain by hand.
// Code generated automatically to be filled manually
// This file will not be updated by generator
// It's created just the first time as a reference
// Generator will allways create a .reference file to help with
// new methods and so
// This file will be included in _gen.cpp
// (anywhere else)
// to make happy some IDEs
As you can see, it is on anonymous namespace
.
This file is private, it will be included by _gen.cpp
. Defining an anonymous
namespace
, we keep this implementation as private. Even more important, the compiler
will alert us if we forget an implementation, also if one is not necessary.
Log and status change transitions are templates.
This is a log example:
[init] rq_key -> w_login
[w_login] rq_login(valid) -> login
[login] rq_logout -> logout
In this way, you can specialize or generalize as much as you want.
// log
void
// status change functions
std::variant<TO, st_error_t>
st_error_t
First parameter in log, is an string with transition change information (initial transition, input, guard if so, final transition)
Next, we have the guards and actions functions.
// guards
bool
bool
bool
// actions
void
void
void
void
} // namespace anonymous
Diagrams source
digraph G