1 /** 2 * Implements a simple reader for Command Sequence Unoriginal Format 3 * 4 * Authors: 5 * Richard Andrew Cattermole 6 * 7 * License: 8 * Copyright Richard Andrew Cattermole 2004 - 2006. 9 * Distributed under the Boost Software License, Version 1.0. 10 * (See accompanying file LICENSE_1_0.txt or copy at 11 * http://www.boost.org/LICENSE_1_0.txt) 12 */ 13 module csuf.reader; 14 import std.traits : isSomeString; 15 16 /// 17 struct CommandSequenceReader(String) if (isSomeString!String) { 18 private { 19 String[] allArgsCommands, allArgsInformation, allArgsInformationCommands; 20 Item!String[] allCommands, allInformation, allInformationCommands; 21 } 22 23 /// 24 Entry!String[] entries; 25 26 /// 27 this(String sourceText, String resetCommand = "new") { 28 import std.algorithm : sum, map, count, splitter; 29 import std..string : lineSplitter, strip; 30 31 auto countCommands = sourceText 32 .lineSplitter 33 .map!strip 34 .map!(line => (line.length > 0 && line[0] == '.' && ((line.length > 1 && line[1] != '.') || line.length == 1)) ? 1 : 0) 35 .sum; 36 auto countInformationCommands = sourceText 37 .lineSplitter 38 .map!strip 39 .map!(line => (line.length > 1 && line[0] == '.' && line[1] == '.') ? 1 : 0) 40 .sum; 41 auto countInformation = sourceText 42 .lineSplitter 43 .map!strip 44 .map!(line => (line.length > 0) ? 1 : 0) 45 .sum - countCommands; 46 47 auto countEntries = sourceText 48 .lineSplitter 49 .map!strip 50 .map!(line => (line.length > resetCommand.length && line[0] == '.' && line[1 .. resetCommand.length+1] == resetCommand) ? 1 : 0) 51 .sum; 52 auto countArgsCommand = sourceText 53 .lineSplitter 54 .map!strip 55 .map!(line => (line.length > 0 && line[0] == '.' && ((line.length > 1 && line[1] != '.') || line.length == 1)) ? line.count(' ') : 0) 56 .sum; 57 auto countArgsInformation = sourceText 58 .lineSplitter 59 .map!strip 60 .map!(line => (line.length > 0 && line[0] != '.') ? line.count(' ') : 0) 61 .sum; 62 auto countArgsInformationCommand = sourceText 63 .lineSplitter 64 .map!strip 65 .map!(line => (line.length > 1 && line[0] == '.' && line[1] == '.') ? line.count(' ') : 0) 66 .sum; 67 68 entries.length = countEntries + 1; 69 70 allCommands.length = countCommands + 1; 71 allInformation.length = countInformation + 1; 72 allInformationCommands.length = countInformationCommands + 1; 73 74 allArgsCommands.length = countArgsCommand + 1; 75 allArgsInformation.length = countArgsInformation + 1; 76 allArgsInformationCommands.length = countArgsInformationCommand + 1; 77 78 Entry!String* entry; 79 80 size_t offsetEntry, offsetCommand, offsetInformation, offsetInformationCommand, 81 offsetCommandArg, offsetInformationArg, offsetInformationCommandArg; 82 83 bool lastWasCommand, lastWasInformationCommand, needInfoCmdReset; 84 foreach(line; sourceText.lineSplitter) { 85 line = line.strip; 86 87 if (line.length > 0) { 88 uint idx; 89 90 foreach(v; line.splitter(' ')) { 91 if (idx == 0) { 92 if (entry !is null) { 93 if (lastWasCommand) { 94 offsetCommandArg += entry.commands[$-1].args.length; 95 } else if (lastWasInformationCommand) { 96 offsetInformationCommandArg += entry.information[$-1].commands[$-1].args.length; 97 } else { 98 offsetInformationArg += entry.information[$-1].args.length; 99 } 100 } 101 102 if (v[0] == '.') { 103 if (entry !is null && v.length > 1 && v[1] == '.') { 104 // ..token 105 106 lastWasCommand = false; 107 lastWasInformationCommand = true; 108 needInfoCmdReset = true; 109 110 if (entry.commands is null) { 111 entry.information[$-1].commands = allInformationCommands[offsetInformationCommand .. offsetInformationCommand + 1]; 112 } else { 113 entry.information[$-1].commands = allInformationCommands[offsetInformationCommand .. offsetInformationCommand + 1 + entry.information[$-1].commands.length]; 114 } 115 116 entry.information[$-1].commands[$-1].name = v[2 .. $]; 117 } else if (v[1 .. $] == resetCommand) { 118 // .new 119 120 if (entry !is null) { 121 offsetCommand += entry.commands.length; 122 offsetInformation += entry.information.length; 123 124 if (needInfoCmdReset) { 125 needInfoCmdReset = false; 126 offsetInformationCommand += entry.information[$-1].commands.length; 127 } 128 } 129 130 entry = &entries[offsetEntry++]; 131 goto DotToken; 132 } else if (entry !is null) { 133 // .token 134 DotToken: 135 136 lastWasCommand = true; 137 lastWasInformationCommand = false; 138 139 if (entry.commands is null) { 140 entry.commands = allCommands[offsetCommand .. offsetCommand + 1]; 141 } else { 142 entry.commands = allCommands[offsetCommand .. offsetCommand + 1 + entry.commands.length]; 143 } 144 145 entry.commands[$-1].name = v[1 .. $]; 146 } 147 } else { 148 // token 149 150 lastWasCommand = false; 151 lastWasInformationCommand = false; 152 153 if (needInfoCmdReset) { 154 needInfoCmdReset = false; 155 offsetInformationCommand += entry.information[$-1].commands.length; 156 } 157 158 if (entry.information is null) { 159 entry.information = allInformation[offsetInformation .. offsetInformation + 1]; 160 } else { 161 entry.information = allInformation[offsetInformation .. offsetInformation + 1 + entry.information.length]; 162 } 163 164 entry.information[$-1].name = v; 165 } 166 } else { 167 if (lastWasCommand) { 168 if (entry.commands[$-1].args is null) { 169 entry.commands[$-1].args = allArgsCommands[offsetCommandArg .. offsetCommandArg + 1]; 170 } else { 171 entry.commands[$-1].args = allArgsCommands[offsetCommandArg .. offsetCommandArg + entry.commands[$-1].args.length + 1]; 172 } 173 174 entry.commands[$-1].args[$-1] = v; 175 } else if (lastWasInformationCommand) { 176 assert(entry.information[$-1].commands.length > 0); 177 178 if (entry.information[$-1].commands[$-1].args is null) { 179 entry.information[$-1].commands[$-1].args = allArgsInformationCommands[offsetInformationCommandArg .. offsetInformationCommandArg + 1]; 180 } else { 181 entry.information[$-1].commands[$-1].args = allArgsInformationCommands[offsetInformationCommandArg .. offsetInformationCommandArg + entry.information[$-1].commands[$-1].args.length + 1]; 182 } 183 184 entry.information[$-1].commands[$-1].args[$-1] = v; 185 } else { 186 if (entry.information[$-1].args is null) { 187 entry.information[$-1].args = allArgsInformation[offsetInformationArg .. offsetInformationArg + 1]; 188 } else { 189 entry.information[$-1].args = allArgsInformation[offsetInformationArg .. offsetInformationArg + entry.information[$-1].args.length + 1]; 190 } 191 192 entry.information[$-1].args[$-1] = v; 193 } 194 } 195 196 idx++; 197 } 198 } 199 } 200 201 entries.length--; 202 allCommands.length--; 203 allInformation.length--; 204 allArgsCommands.length--; 205 allArgsInformation.length--; 206 allArgsInformationCommands.length--; 207 } 208 209 @disable 210 this(this); 211 } 212 213 /// 214 struct Entry(String) if (isSomeString!String) { 215 /// 216 Item!String[] commands; 217 /// 218 Item!String[] information; 219 } 220 221 /// 222 struct Item(String) if (isSomeString!String) { 223 import std.traits : isPointer; 224 225 /// 226 String name; 227 /// 228 String[] args; 229 230 Item!String[] commands; 231 232 /// 233 T get(T)(size_t offset, T default_ = T.init) if (!(is(T == struct) || is(T == class) || is(T == union) || isPointer!T)) { 234 import std.conv : to; 235 if (args.length > offset) { 236 return to!T(args[offset]); 237 } else { 238 return default_; 239 } 240 } 241 } 242 243 /// 244 unittest { 245 CommandSequenceReader!string reader = CommandSequenceReader!string(" 246 .new 247 248 Hi <name> 249 250 .new 8 251 .something back 6 252 253 Bye <name> loozer 254 255 ", "new"); 256 } 257 258 unittest { 259 CommandSequenceReader!dstring reader = CommandSequenceReader!dstring(" 260 .new 261 262 Hi <name> 263 264 .new 8 265 .something back 6 266 267 Bye <name> loozer 268 269 "d, "new"d); 270 } 271 272 /// 273 unittest { 274 CommandSequenceReader!string reader = CommandSequenceReader!string(" 275 .new 276 277 Hi <name> 278 279 .new 8 280 .something back 6 281 282 Bye <name> loozer 283 ..myend here! 284 285 .\\.a command 286 ", "new"); 287 } 288 289 /// 290 unittest { 291 CommandSequenceReader!string reader = CommandSequenceReader!string(" 292 .new HEADER 293 .something here 294 295 .new 296 abcd 297 ", "new"); 298 }