Releases: brucou/slim
CLI
Smaller compiled sizes, supporting graph decluttering, increased expressivity
Two keys features for this release:
- The grammar has been improved to:
- permit multiple machine transitions on the same graph edge
- handle composite actions and guards
- compiler shaves another 15% on average and large machines. A 75 -transition machine now compiles down to 2.3 KB
So, you can now write edge labels as:
event [cond1, cond2] / action1, action2 (comment)
: When event occurs and both conditions are satisfied, the machine will run both actions.comment
is ignored.| e [g] / a | e' [g'] / a'
: this is equivalent to having two separate edges withe [g] / a
ande' [g'] / a'
as labels. The rationale behind this feature is to allow modelers to group edges logically. Also, the feature helps reduce the visual clutter that occurs when a lot of edges goes from one source to the same destination.
cf. docs
Improved graph parser
The parser for edge labels is an important piece of the compiler. An EBNF grammar is now used to parse labels. Parser used is nearley.
In short:
event [guard] / actions
event
cannot have the characters '[', ']', and '/'guard
cannot have the characters '[', ']'actions
cannot have the characters '/'
The rules serve to disambiguate the grammar both for the parser, but also to make easy for the compiler user to not write erroneous labels inadvertently.
Grammar is:
MAIN ->
EventClause "[" GuardClause "]" _ "/" ActionsClause {% d => ({event: d[0][0].join(''), guard: d[2][0].join(''), actions: d[6][0].join('')}) %}
| EventClause "[" GuardClause "]" _ {% d => ({event: d[0][0].join(''), guard: d[2][0].join(''), actions: ""}) %}
| EventClause "/" ActionsClause {% d => ({event: d[0][0].join(''), guard: "", actions: d[2][0].join('')}) %}
| EventClause {% d => ({event: d[0][0].join(''), guard: "", actions: ""}) %}
| "[" GuardClause "]" _ "/" ActionsClause {% d => ({event: "", guard: d[1][0].join(''), actions: d[5][0].join('')}) %}
| "/" ActionsClause {% d => ({event: "", guard: "", actions: d[1][0].join('')}) %}
| "[" GuardClause "]" {% d => ({event: "", guard: d[1][0].join(''), actions: ""}) %}
EventClause -> [^\/\[\]]:+
GuardClause -> [^\[\]]:*
ActionsClause -> [^\/]:*
StringLiteral -> [a-zA-z]:+
_ -> [\s]:*
Optimized size of compiled file
- Better ergonomy for compiler user
- now the compiled file starts with comments that the user can copy paste, and fill in with the guards and actions. This avoids errors linked to mistyping the strings that are used as control state names, action names, or guard names.
- changed the algorithm to reduce compiled program size. Medium's clone compiles under 3.3KB with ~50 states, and ~100 transitions
Additional compiler optimizations
Added optimizations to reduce file size:
INIT_STATE
,INIT_EVENT
are replaced by their valuesinitialControlState
,initialExtendedState
are replaced by their values- action identity functions are optimized
- case of graph without hierarchy optimized
- other code cleaning/simplification
fixed bug with settings
In the previous versions, the factory admitted a settings parameter to allow to inject dependencies in the machine. However that parameter was not properly passed to the actions and guards. This is now fixed.
Optimization for machines without automatic transitions
This version includes optimized code for machines which do not use automatic transitions, i.e.:
- no transition to compound states
- no transition to history states
- no eventless transitions
Optimization
Optimized compiled code for state machines which do not use history states
change of package name
Temporary name lesser
changed to originally intended name slim
(package name was taken and have been since liberated)
Compiler for Kingly state machines
The compiler generates code that removes the cruft added by the library. This involves among other things error management, and logging. The hypothesis here is that you are compiling a machine that you have previously tested and debugged (with the debug and devtool settings).
Further possible optimizations are;
- remove the history state updates if there is no history pseudo states
- remove processing of the next events if there is no automatic events
- i.e. no compound states (even better, no transitions towards compound states), and no eventless transitions
Optimizations in base of action content are not deemed worthwhile.