1 module cushion.stmgen;
2 
3 import std.array, std.format, std..string, std.algorithm;
4 import cushion.core;
5 
6 /*******************************************************************************
7  * Generator of STM
8  */
9 package(cushion) struct StmGenerator
10 {
11 	///
12 	string         stmFileName;
13 	///
14 	string         mapFileName;
15 	
16 	///
17 	string[string] map;
18 	
19 	///
20 	string         nameRaw;
21 	///
22 	string[]       statesRaw;
23 	///
24 	string[]       eventsRaw;
25 	///
26 	string[][]     cellsRaw;
27 	///
28 	string[]       stactsRaw;
29 	///
30 	string[]       edactsRaw;
31 	
32 	///
33 	string[]       states;
34 	///
35 	string[]       events;
36 	///
37 	string[][][]   procs;
38 	///
39 	string[][]     nextsts;
40 	///
41 	string[][]     stacts;
42 	///
43 	string[][]     edacts;
44 	
45 	///
46 	string         stateKey = "▽";
47 	///
48 	string         factoryName = "makeStm";
49 	
50 	
51 	/***************************************************************************
52 	 * 
53 	 */
54 	string genCode()
55 	{
56 		auto srcstr = appender!string();
57 		makeEnumStates(srcstr);
58 		makeEnumEvents(srcstr);
59 		makeActivities(srcstr);
60 		makeProcs(srcstr);
61 		makeFactory(srcstr);
62 		return srcstr.data();
63 	}
64 private:
65 	// 状態の書き出し。 State という名前の enum を書き出す
66 	void makeEnumStates(Range)(ref Range srcstr)
67 	{
68 		auto app = appender!(string[])();
69 		foreach (s; statesRaw)
70 		{
71 			auto str = map.get(s, s);
72 			if (str.length)
73 				app.put( str );
74 		}
75 		
76 		states = app.data;
77 		srcstr.formattedWrite!(q{
78 			static import cushion.core;
79 			static import std.traits;
80 			static if (__traits(compiles, StateTransitor)
81 			        && std.traits.isInstanceOf!(cushion.core.StateTransitor, StateTransitor)
82 			        && !__traits(compiles, State)
83 			        && is(State == enum))
84 			{
85 				static assert([__traits(allMembers, StateTransitor.State)] == [
86 			%-(		"%s"%|,
87 			%)]);
88 				alias State = StateTransitor.State;
89 			}
90 			else static if (__traits(compiles, State) && is(State == enum))
91 			{
92 				static assert([__traits(allMembers, State)] == [
93 			%-(		"%s"%|,
94 			%)]);
95 			}
96 			else
97 			{
98 				enum State
99 				{
100 			%-(		%s,
101 			%)
102 				}
103 			}
104 		}.outdent)(states, states, states);
105 	}
106 	
107 	// イベントの書き出し。 Event という名前の enum を書き出す。
108 	void makeEnumEvents(Range)(ref Range srcstr)
109 	{
110 		auto app = appender!(string[])();
111 		foreach (s; eventsRaw)
112 		{
113 			auto str = map.get(s, s);
114 			if (str.length)
115 				app.put( str );
116 		}
117 		
118 		events = app.data;
119 		srcstr.formattedWrite!(q{
120 			static import cushion.core;
121 			static import std.traits;
122 			static if (__traits(compiles, StateTransitor)
123 			        && std.traits.isInstanceOf!(cushion.core.StateTransitor, StateTransitor)
124 			        && !__traits(compiles, Event)
125 			        && is(Event == enum))
126 			{
127 				static assert([__traits(allMembers, StateTransitor.Event)] == [
128 			%-(		"%s"%|,
129 			%)]);
130 				alias Event = StateTransitor.Event;
131 			}
132 			else static if (__traits(compiles, Event) && is(Event == enum))
133 			{
134 				static assert([__traits(allMembers, Event)] == [
135 			%-(		"%s"%|,
136 			%)]);
137 			}
138 			else
139 			{
140 				enum Event
141 				{
142 			%-(		%s,
143 			%)
144 				}
145 			}
146 		}.outdent)(events, events, events);
147 	}
148 	
149 	static void replaceProcContents(Range)(ref Range srcstr, ref string[] procs, string[string] map)
150 	{
151 		auto app = appender!(string[])();
152 		foreach (s; procs)
153 		{
154 			auto str = map.get(s, s);
155 			if (str.length)
156 				app.put( str );
157 		}
158 		procs = app.data;
159 	}
160 	
161 	// 
162 	void makeProcs(Range)(ref Range srcstr)
163 	{
164 		procs   = new string[][][](events.length, states.length, 0);
165 		nextsts = new string[][](events.length, states.length);
166 		foreach (i, rows; cellsRaw)
167 		{
168 			foreach (j, cell; rows)
169 			{
170 				auto lines = cell.splitLines();
171 				string nextState;
172 				string[] proclines;
173 				if (lines.length && lines[0].startsWith(stateKey))
174 				{
175 					nextState = map.get(lines[0], lines[0]);
176 					assert(nextState.length);
177 					proclines = lines[1..$];
178 				}
179 				else
180 				{
181 					nextState = states[j];
182 					proclines = lines;
183 				}
184 				
185 				replaceProcContents(srcstr, proclines, map);
186 				procs[i][j] = proclines;
187 				nextsts[i][j] = nextState;
188 				if (proclines.length == 0 || proclines[0] == "x")
189 				{
190 					continue;
191 				}
192 				srcstr.formattedWrite("void _stmProcE%dS%d()\n{\n", i, j);
193 				srcstr.formattedWrite("%-(\t%s\n%)", proclines);
194 				srcstr.put("\n}\n");
195 			}
196 		}
197 	}
198 	
199 	// アクティビティ用の関数を作成する _stmStEdActivity という関数名で作成
200 	void makeActivities(Range)(ref Range srcstr)
201 	{
202 		auto apped = appender!string();
203 		auto appst = appender!string();
204 		
205 		stacts.length = stactsRaw.length;
206 		edacts.length = edactsRaw.length;
207 		
208 		appst.put("\tswitch (newsts)\n\t{\n");
209 		foreach (i, act; stactsRaw)
210 		{
211 			auto proclines = act.splitLines();
212 			if (proclines.length == 0)
213 				continue;
214 			auto name = format("_stmStartActS%d", i);
215 			srcstr.put("void ");
216 			srcstr.put(name);
217 			srcstr.put("()\n{\n");
218 			replaceProcContents(srcstr, proclines, map);
219 			srcstr.formattedWrite("%-(\t%s\n%)", proclines);
220 			srcstr.put("\n}\n");
221 			appst.formattedWrite(
222 				"\tcase cast(typeof(newsts))%d:\n"
223 				 ~ "\t\t%s();\n"
224 				 ~ "\t\tbreak;\n", i, name);
225 			stacts[i] = proclines;
226 		}
227 		appst.put(
228 			"\tdefault:\n"
229 			 ~ "\t}\n");
230 		
231 		apped.put("\tswitch (oldsts)\n\t{\n");
232 		foreach (i, act; edactsRaw)
233 		{
234 			auto proclines = act.splitLines();
235 			if (proclines.length == 0)
236 				continue;
237 			auto name = format("_stmEndActS%d", i);
238 			srcstr.put("void ");
239 			srcstr.put(name);
240 			srcstr.put("()\n{\n");
241 			replaceProcContents(srcstr, proclines, map);
242 			srcstr.formattedWrite("%-(\t%s\n%)", proclines);
243 			srcstr.put("\n}\n");
244 			apped.formattedWrite(
245 				"\tcase cast(typeof(oldsts))%d:\n"
246 				 ~ "\t\t%s();\n"
247 				 ~ "\t\tbreak;\n", i, name);
248 			edacts[i] = proclines;
249 		}
250 		apped.put(
251 			"\tdefault:\n"
252 			 ~ "\t}\n");
253 		if (appst.data.length != 0 || apped.data.length != 0)
254 		{
255 			srcstr.put(
256 				"void _onStEdActivity(State oldsts, State newsts)\n"
257 				 ~ "{\n"
258 				 ~ "\tif (oldsts == newsts)\n"
259 				 ~ "\t\treturn;\n");
260 			srcstr.put( apped.data );
261 			srcstr.put( appst.data );
262 			srcstr.put("}\n");
263 		}
264 	}
265 	
266 	
267 	// 
268 	void makeFactory(Range)(ref Range srcstr)
269 	{
270 		auto app = appender!(string[][])();
271 		auto app2 = appender!(string[])();
272 		
273 		foreach (i; 0..events.length)
274 		{
275 			app2.shrinkTo(0);
276 			foreach (j; 0..states.length)
277 			{
278 				string proc;
279 				if (procs[i][j].length == 0)
280 				{
281 					proc = "ignoreHandler";
282 				}
283 				else if (procs[i][j].length == 1 && procs[i][j][0] == "x")
284 				{
285 					proc = "forbiddenHandler";
286 				}
287 				else
288 				{
289 					proc = format("&_stmProcE%dS%d", i, j);
290 				}
291 				app2.put(format("C(State.%s, %s)", nextsts[i][j], proc));
292 			}
293 			app.put(app2.data.dup);
294 		}
295 		alias existsAct = reduce!"a | (b.length != 0)";
296 		string setActHandler;
297 		if (existsAct(false, stacts) || existsAct(false, edacts))
298 			setActHandler = "stm.setStateChangedHandler(&_onStEdActivity);";
299 		srcstr.formattedWrite!(q{
300 			auto %s() @safe
301 			{
302 				static import cushion.core;
303 				static import std.traits;
304 				static if (__traits(compiles, StateTransitor)
305 				        && std.traits.isInstanceOf!(cushion.core.StateTransitor, StateTransitor))
306 				{
307 					alias _ST = StateTransitor;
308 				}
309 				else static if (__traits(compiles, StateTransitor))
310 				{
311 					alias _ST = StateTransitor!(State, Event);
312 				}
313 				else
314 				{
315 					alias _ST = cushion.core.StateTransitor!(State, Event);
316 				}
317 				alias _ST.Cell C;
318 				auto stm = _ST([
319 					%([%-(%s, %)]%|,
320 					%)]);
321 				%s
322 				stm.matrixName = `%s`;
323 				stm.stateNames = %s;
324 				stm.eventNames = %s;
325 				return stm;
326 			}
327 		}.outdent)(factoryName, app.data, setActHandler, nameRaw, statesRaw, eventsRaw);
328 	}
329 	
330 }
331