diff --git a/vlib/datatypes/fsm/README.md b/vlib/datatypes/fsm/README.md index 8b62a99cd0..567a7b1168 100644 --- a/vlib/datatypes/fsm/README.md +++ b/vlib/datatypes/fsm/README.md @@ -19,3 +19,36 @@ When that happens: After all transitions are checked, and thus the state is changed, the client-specified `on_run()` handler of the now current state is called. + +## Plot States and Transitions +This module includes a tool for generating dot diagrams from .v source code, +that defines a FSM. The tool is located in [fsm_graph.v](tools/fsm_graph.v). + +Here is an example of how to generate a .dot file with the graph and transitions: +```bash +v run vlib/datatypes/fsm/tools/fsm_graph.v -f vlib/datatypes/fsm/fsm_test.v > graph.dot +``` + +You can convert the generated .dot file to a PNG file with Graphviz's `dot` +conversion tool: +```bash +v run vlib/datatypes/fsm/tools/fsm_graph.v -f vlib/datatypes/fsm/fsm_test.v > graph.dot +dot -Tpng graph.dot > graph.png +xdg-open graph.png +``` + +You can also visualise it with Graphviz (the `dot` command) +& ImageMagick (the `display` command): +```bash +v run vlib/datatypes/fsm/tools/fsm_graph.v -f vlib/datatypes/fsm/fsm_test.v | dot -Tpng | display +``` + +To view the .dot file, you can also use any of the +[Graphviz Graphical Interfaces](https://graphviz.org/resources/#graphical-interfaces) +and `xdot` in particular: +```bash +v run vlib/datatypes/fsm/tools/fsm_graph.v -f vlib/datatypes/fsm/fsm_test.v | xdot - +``` + +In all of the above examples, you can replace `vlib/datatypes/fsm/fsm_test.v` +with the path to your own .v code that imports and uses `fsm`. diff --git a/vlib/datatypes/fsm/tools/fsm_graph.v b/vlib/datatypes/fsm/tools/fsm_graph.v new file mode 100644 index 0000000000..996ba01c80 --- /dev/null +++ b/vlib/datatypes/fsm/tools/fsm_graph.v @@ -0,0 +1,44 @@ +import os +import flag + +pub fn read_file(file string) ?[]string { + if os.is_file(file) { + text := os.read_lines(file) or { + return error(@MOD + '.' + @STRUCT + '.' + @FN + ' Could not read "$file": "$err.msg()"') + } + return text + } + return [''] +} + +pub fn extract_transitions(line string) ?string { + mut result := ' ' + first_comma := line.index(',') ? + second_comma := line.index_after(',', first_comma + 1) + + from := line[..first_comma] + to := line[first_comma + 1..second_comma] + condition := line[second_comma + 1..] + + return result + from + ' -> ' + to + ' [label=' + condition + '];' +} + +pub fn get_transitions(line string) ?string { + mut raw_text := line[line.index_any('(') + 1..line.index_any(')')] + raw_text = raw_text.replace("'", '').replace(' ', '') + return extract_transitions(raw_text) +} + +pub fn main() { + mut fp := flag.new_flag_parser(os.args) + file := fp.string('file', `f`, '', 'input V file with transitions to generate graph from.') + lines := read_file(file) ? + println('digraph fsm {') + for line in lines { + if line.contains('add_transition') { + println(get_transitions(line) ?) + } + } + + println('}') +}