cargo install -f fsm_gen
You can download from repository and build and install as usual
cargo build --release
cargo install --path . -f
-
revisit dot
-
generate mermaid
-
check README.md
-
Error transition is special
-
Inprove exceptions control
-
web ui for templates
-
read template from file
-
Complete the cpp example and update on README.md
-
Check fsm format
- detect duplicated states
- detect duplicated inputs and guards
-
output with signals
-
Add new templates
- Added adapters (aka transformers)
- New cpp generator with no c++ template<> code
- cpp template with CamelNames for structs
- "_" special final state
- Modifs on
cpp
generated code- Removed
.reference
files - Added
_forwards
files gen_
at the begining to indicate you souldn't touchactions
andguards
usingcpp templates
- Removed
- Better
.dot
generated diagrams - cpp
- Removed shadow warning
- Removed no virtual destructor
cpp
generated code with option to move toerror
state- Instead of
languages
, now we speak abouttemplates
- moved to tera templates
- added dot files generation
- 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
- 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
- Actions
- private hpp (hand file)
- Template functions to specialize on transaction change
- Anonymous
namespace
for functions - Add comments support on fsm grammar
All computer systems interact with the external world by receiving information, processing it, and generating a result.
In addition to communicating with the outside world through input/output
, it is common for a system to manage a state
.
These two elements are fundamental in computer systems.
That's why I developed two external DSLs (for data and states) several years ago to simplify working with these concepts (which I still use in production and find very helpful).
In this repository, I have rewritten one of them: a code generator for state machines.
Currently, it generates C++ code (the most practical to me at the moment ;-).
To explain the system, I will use the example in cpp_test/fsm.
This example is about building a system that handles login requests.
The state diagram would look like this:
First, the client will require a key to send a request login with a coded password.
This key will be used to encrypt the username and password (the password will go through a hash function) in the login request.
This encryption is irreversible (hash function). The server will perform the same operation (starting from the password hash) to verify its validity.
If it is correct, a login confirmation will be sent.
The DSL representation of this definition would be:
// Example of an FSM for managing login
// on the server side
[init]
rq_key -> w_login / send_key // this is a comment
timer -> init
[w_login]
rq_login
& ok -> login / send_login
-> error
timer
& timeout -> error
-> w_login
[login]
rq_logout -> logout / send_logout
heartbeat -> login / update_hbr
timer
& timeout -> logout
-> login
[logout]
timer -> _
_ -> testing / send_logout
[error]
_ -> error
This program also generates the displayed diagram before using the DSL
.
Let's now break down the elements that define a state machine.
A state machine specifies the states and transitions based on the received inputs.
The machine is always in a state, and for each received input and the contextual information of the current state, we transition to another state, potentially performing some actions.
Therefore, in each state, there will be specific contextual information associated with that state.
[w_login]
rq_login -> login / send_login
Starting from the state w_login, if we receive the login request, we transition to the state login and send the login confirmation (send_login).
In this simple transition, we can see the basic elements:
initial estate
v
[w_login]
INPUT FINAL STATE ACTION
v v v
rq_login -> login / send_login
[init]
- Enclosed in brackets
- At the beginning of the line
Received element
[w_login]
INPUT
v
rq_login -> login / send_login
[w_login]
FINAL STATE
v
rq_login -> login / send_login
Where the machine will be positioned for processing the next input
[w_login]
ACTION
v
rq_login -> login / send_login
In addition to transitioning to the new state, we can specify what action/effect to do on each transition
Multiple actions can be define for each transition by separating them with spaces
[w_login]
rq_login -> login / send_login write_log
Guards are used to define different paths based on the state information and input
GUARD
v
rq_login & ok -> login / send_login
You can have more than one guard.
GUARD GUARD
v v
rq_login & ok & system_ready -> login / send_login
For an input, you can for using several guards to generate different transitions
When last transition doesn't have guard, behave as else
rq_login
& valid -> login / send_login
-> logout / log_err
You can use negation with guards
The previous example could be written with a negative guard.
rq_login
NEGATED GUARD
v
& !valid -> logout / log_err
-> login / send_login
In the following transition, we have a special input "_":
[logout]
_ -> testing / send_logout
^
SPECIAL INPUT
The symbol "_" will be used to indicate that a transition should be generated with the rules defined in this one for each unspecified input.
[logout]
timer -> _
^
SPECIAL FINAL STATE
_ -> testing / send_logout
A final state with the symbol "_" indicates that there is no change in state.
Not only do we stay in the same state, but the state's context data remains unchanged.
error
is a special status
You can move to error
status explicitly as any other normal status
But any transition no defined, will finish 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 noise
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
aka transformers
Adapters can be placed at the input, guards, and transition levels.
In the case of transitions, they will be placed in the final state.
ADAPTER
[init] v
rq_key -> w_login / send_key|rq
timer -> init
_ -> logout / log_err
The adapters will help us extract common functionalities, reducing the number of functions to be filled manually, or adding context information to make the generated code more readable.
ADAPTER ADAPTER ADAPTER
[init] v v v
rq_key &ok|rq -> w_login|rqok / send_key|rq
timer -> init
_ -> logout / log_err
Comments start with // and continue until the end of the line.
To get help...
fsm_gen --help
> fsm_gen -h
fsm_gen 0.8.0
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
-f, --force Generate all files regardless of change date. DANGEROUS!!!
-h, --help Prints help information
--help-cpp Give me some information about generating cpp files with no templates to fill
-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 cpp
(for c++) (and at the moment the only one)
fsm_gen --help
> fsm_gen --show-templs
Supported templates: cpp
fsm_gen --help
Help about cpp template...
> fsm_gen --help-cpp
Being "name.fsm" the name of the file with the definition of the machine, next files an directories will be generated
All will be generated on directory...
fsm_<name>/
Main entry point. Do not modify, it will be rewritting on each execution
<name>.h
<name>.cpp
Types
<name>_types.h (to be filled manually)
<name>_types_reference.h (just as reference)
<name>_types_adapters_fordward.h (forward declaration and reference)
<name>_types_adapters.h (to be filled manually)
Support functions.
You shouldn't mofify headers and you have to fill .cpp
impl/log.hpp
impl/guards.h
impl/guards.cpp
impl/actions.h
impl/actions.cpp
impl/transitions.h
impl/transitions.cpp
To generate code... run
fsm_gen login.fsm
And it will generate the c++
You can pass a list of fsm
files
fsm_gen login.fsm test/seller.fsm test/test2/lift.fsm
The code will be generated on same directory of original .fsm
file
If your shell supports it, you could run...
fsm_gen **/*.fsm
It will also generate the files types.h, types_adapters.h, and types_forward.h.
The types_reference.h file serves as a reference for everything generated in the types files.
The types.h file is initially created and will not be overwritten in subsequent executions.
This file will be used to customize the "types".
Directories/modules will be created for
impl
actions
guards
transitions
The header files will be rewritten and will help avoid "dead code" and provide the new functions to be filled in.
transitions
, guards
and adapters
should be pure, no effects
transitions
will return the new transition with provider parameters (from status, intput)guards
: will return true or false with current status and inputadapters
will return a new type using the parameters where the adapter is appliedactions
with initial transition status, the input and the final status, we have the oportunity to do something (in general, produce an effect)
When an adapter
is applied to a transition
, guard
, or action
, the parameter will be the adatpter
type
The adapters
will be C++ structs with constructors based on the context.
In these types, we have the opportunity to add direct or processed information.
The adapters will help us extract common factors, reducing the number of functions to be filled in, or add context information to make the generated code more readable.