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