decodeStmFromCsv

Decode to D language code from STM of CSV

In the first argument, STM in CSV format is passed as a string. And in the second argument, replacement map in CSV format is passed as a string. This CSV pair will be decoded into the code in D language.

More...
string
decodeStmFromCsv
(
,,
string mapFileName = null
,
string stmFileName = null
,
string stateKey = "▽"
,
string factoryName = "makeStm"
)

Detailed Description

STM by CSV is converted according to the following rules.

  • The cell at most top-left is described name of the STM.
  • In the first row cells except the leftmost column, "state" names are described.
  • Cells of leftmost column in rows 2 and 3 are ignored in program code.
  • "Event" names are described on the leftmost 4th row and beyond.
  • The 1st line of CSV describes "states"
  • "States" is always specified a string beginning with "∇" (default character) or stateKey that is user defined key character.
  • The 2nd line of CSV describes "start activity"
  • The 3rd line of CSV describes "end activity"
  • "start activity" and "end activity" are described by some "processes"
  • When the "state transitions", the "process" described in the "start activity" of the "states" of after the transition are performed.
  • In the other hand, the "processes" in the "end activity" are performed at before the transition.
  • In cell where "state" and "event" intersect, "processes" are described
  • For state transition, specify the state name starting with stateKey in the first line of the "processes" described in the cell
  • A blank cell does not process the event and means to ignore it.
  • Cells written with only x assert forbidden event handling.

The replacement map is described according to the following specifications.

  • The replacement target of the replacement map CSV is the "state", "event", "state transition", and "process" of the STM
  • The first column of CSV describes the string before conversion.
  • The second column of CSV describes the string after conversion.
  • The string in the left column is simply replaced by the string in the right column.
  • After substitution, "process" must be complete D language code.
  • "event" must be replaced to enum member of "Event".
  • "state" must be replaced to enum member of "State".
  • And also, "state transition" that are described together "process" must be replaced to enum member of "State".

Since the string generated by this function is the source code of the D language, it can be embedded in the actual code after saving it in the file, or it can be used directly by mixin(). The "states" are generated as an enum type named State, and the "events" are generated as an enum type named Event. STM instance is generated by executing the generated factory function that name can be specified by factoryName with makeStm as its default name.

Examples

Example is following.

This case explains how to operate the music player with the start button and the stop button. The players play music when the start button is pressed while stopping. And when you press the start button during playing, the behavior will change and music playback will pause. When the stop button is pressed, the player stops music playback and returns to the initial state.

When this specification is made to STM, the following table can be created.

stmcsv:

*MusicPlayer*#>stop#>play#>pause
StartAct.
EndAct.
onStart#>play
- Start music
#>pause
- Stop music
#>play
- Start music
onStop#>stop
- Stop music
- Return to first
#>stop
- Return to first

In each cell of the table, the transition destination and processing are described in natural language. Representations in natural language are replaced by the following map table and converted into a program expression in D. One line in each cell is subject to replacement. However, those that do not exist in the replacement map are not replaced.

mapcsv:

#>stopstop
#>playplay
#>pausepause
- Start musicstartMusic();
- Stop musicstopMusic();
- Return to firstresetMusic();

To execute the pair of STM and replacement map as code, see the following code:

1 import std.string, std.datetime.stopwatch;
2 // STM
3 enum stmcsv = `
4 *MusicPlayer*,#>stop,#>play,#>pause
5 StartAct,,,
6 EndAct,,,
7 onStart,"#>play\n- Start music","#>pause\n- Stop music","#>play\n- Start music"
8 onStop,,"#>stop\n- Stop music\n- Return to first","#>stop\n- Return to first"`
9 .strip("\n").outdent.replace(`\n`,"\n");
10 
11 // replacement mapping data
12 enum mapcsv = `
13 #>stop,stop
14 #>play,play
15 #>pause,pause
16 - Start music,startMusic();
17 - Stop music,stopMusic();
18 - Return to first,resetMusic();`
19 .strip("\n").outdent.replace(`\n`,"\n");
20 
21 // Programs to be driven by STM
22 string status = "stopped";
23 StopWatch playTime;
24 void startMusic() { playTime.start(); status = "playing"; }
25 void stopMusic()  { playTime.stop();  status = "stopped"; }
26 void resetMusic() { playTime.reset(); }
27 
28 // Generate code from STM(csv data) and mapping data
29 enum stmcode = decodeStmFromCsv(stmcsv, mapcsv, null, null, "#>", "makeStm");
30 // string mixin. Here the code is expanded.
31 // The code contains the enum of State and Event,
32 // activity functions and proccess when transtition.
33 mixin(stmcode);
34 
35 // Create StateTransitor instance
36 // By executing this function, construction of StateTransitor,
37 // registration of various handlers, name setting of the matrix and states / events are performed.
38 auto stm = makeStm();
39 
40 // Initial state is "stop" that most left state.
41 assert(stm.currentState == State.stop);
42 // At run time, display names of the state are gettable
43 assert(stm.getStateName(stm.currentState) == "#>stop");
44 // Likewise, event names are also gettable
45 // In this case, event names are not replaced by mapcsv datas,
46 // this code in following line gets the same string as the enum member of Event.
47 assert(stm.getEventName(Event.onStart) == "onStart");
48 
49 // When the onStart event occurs, based on the STM,
50 // it transit to the "#>play" state and play music.
51 stm.put(Event.onStart);
52 assert(stm.currentState == State.play);
53 assert(playTime.running);
54 () @trusted { import core.thread; Thread.sleep(10.msecs); }(); // progress in playing...
55 assert(playTime.peek != 0.msecs);
56 
57 // If push the play button again during playing, it pauses.
58 stm.put(Event.onStart);
59 assert(stm.currentState == State.pause);
60 assert(!playTime.running);
61 
62 // When you press the stop button, the player stops and returns to the first stop state
63 stm.put(Event.onStop);
64 assert(stm.currentState == State.stop);
65 assert(!playTime.running);
66 () @trusted { import core.thread; Thread.sleep(10.msecs); }(); // progress in stopped...
67 assert(playTime.peek == 0.msecs);

Following example is case of network communication in Japanese.

1 import std.string;
2 enum stmcsv = `
3 ,▽初期,▽接続中,▽通信中,▽切断中
4 スタートアクティビティ,,接続要求を開始,,切断要求を開始
5 エンドアクティビティ,,接続要求を停止,,切断要求を停止
6 接続の開始指示を受けたら,▽接続中,,x,x
7 接続の停止指示を受けたら,,▽切断中,▽切断中,
8 通信が開始されたら,▽切断中,▽通信中,x,x
9 通信が切断されたら,x,▽初期,▽初期,▽初期`
10 .strip("\n").outdent.replace(`\n`,"\n");
11 
12 enum replaceData = `
13 ▽初期,init
14 ▽接続中,connectBeginning
15 ▽通信中,connecting
16 ▽切断中,connectClosing
17 通信が開始されたら,openedConnection
18 通信が切断されたら,closedConnection
19 接続の開始指示を受けたら,openConnection
20 接続の停止指示を受けたら,closeConnection
21 接続要求を開始,startBeginConnect();
22 接続要求を停止,endBeginConnect();
23 切断要求を開始,startCloseConnect();
24 切断要求を停止,endCloseConnect();`
25 .strip("\n").outdent.replace(`\n`,"\n");
26 
27 enum stmcode = decodeStmFromCsv(stmcsv, replaceData);
28 int x;
29 void startBeginConnect()
30 {
31 	x = 1;
32 }
33 void endBeginConnect()
34 {
35 	x = 2;
36 }
37 void startCloseConnect()
38 {
39 	x = 3;
40 }
41 void endCloseConnect()
42 {
43 	x = 4;
44 }
45 
46 mixin(stmcode);
47 auto stm = makeStm();
48 assert(stm.getStateName(State.init) == "▽初期");
49 assert(stm.getStateName(stm.currentState) == "▽初期");
50 assert(x == 0);
51 stm.put(Event.openConnection);
52 assert(x == 1);
53 assert(stm.getStateName(stm.currentState) == "▽接続中");
54 assert(stm.currentState == State.connectBeginning);
55 stm.put(Event.openedConnection);
56 assert(x == 2);
57 assert(stm.getStateName(stm.currentState) == "▽通信中");
58 assert(stm.currentState == State.connecting);
59 stm.put(Event.closeConnection);
60 assert(x == 3);
61 assert(stm.getStateName(stm.currentState) == "▽切断中");
62 assert(stm.currentState == State.connectClosing);
63 stm.put(Event.closedConnection);
64 assert(x == 4);
65 assert(stm.getStateName(stm.currentState) == "▽初期");
66 assert(stm.currentState == State.init);

Meta