| 227 | | template store(A...) { |
|---|
| 228 | | alias A a; |
|---|
| 229 | | } |
|---|
| 230 | | template decRecurse(char[] A, B...) { |
|---|
| 231 | | static if (B.length) const char[] decRecurse = A ~ ", " ~ decRecurse!(B); |
|---|
| 232 | | else const char[] decRecurse = A; |
|---|
| 233 | | } |
|---|
| 234 | | template decBool(A...) { |
|---|
| 235 | | const char[] decBool = "bool " ~ decRecurse!(A) ~ ";\n"; |
|---|
| 236 | | } |
|---|
| 237 | | template decInt(A...) { |
|---|
| 238 | | const char[] decInt = "int " ~ decRecurse!(A) ~ ";\n"; |
|---|
| 239 | | } |
|---|
| 240 | | template decDouble(A...) { |
|---|
| 241 | | const char[] decDouble = "double " ~ decRecurse!(A) ~ ";\n"; |
|---|
| 242 | | } |
|---|
| 243 | | template decCharA(A...) { |
|---|
| 244 | | const char[] decCharA = "char[] " ~ decRecurse!(A) ~ ";\n"; |
|---|
| 245 | | } |
|---|
| 246 | | template aaRecurse(char[] A, B...) { |
|---|
| 247 | | static if (B.length) const char[] aaRecurse = "\""~A~"\"[]:&"~A ~ ", " ~ aaRecurse!(B); |
|---|
| 248 | | else const char[] aaRecurse = "\""~A~"\"[]:&"~A; |
|---|
| 249 | | } |
|---|
| 250 | | template aaBool(A...) { |
|---|
| 251 | | const char[] aaBool = "optsBool = [" ~ aaRecurse!(A) ~ "];\n"; |
|---|
| 252 | | } |
|---|
| 253 | | template aaInt(A...) { |
|---|
| 254 | | const char[] aaInt = "optsInt = [" ~ aaRecurse!(A) ~ "];\n"; |
|---|
| 255 | | } |
|---|
| 256 | | template aaDouble(A...) { |
|---|
| 257 | | const char[] aaDouble = "optsDouble = [" ~ aaRecurse!(A) ~ "];\n"; |
|---|
| 258 | | } |
|---|
| 259 | | template aaCharA(A...) { |
|---|
| 260 | | const char[] aaCharA = "optsCharA = [" ~ aaRecurse!(A) ~ "];\n"; |
|---|
| 261 | | } |
|---|
| | 228 | private { |
|---|
| | 229 | // Return index of first comma, or halts if not found. |
|---|
| | 230 | template cIndex(char[] A) { |
|---|
| | 231 | static if (A.length == 0) |
|---|
| | 232 | static assert (false, "Error in implementation"); |
|---|
| | 233 | else static if (A[0] == ',') |
|---|
| | 234 | const size_t cIndex = 0; |
|---|
| | 235 | else |
|---|
| | 236 | const size_t cIndex = 1 + cIndex!(A[1..$]); |
|---|
| | 237 | } |
|---|
| | 238 | // Return index of first semi-colon, or halts if not found. |
|---|
| | 239 | template scIndex(char[] A) { |
|---|
| | 240 | static if (A.length == 0) |
|---|
| | 241 | static assert (false, "Error: no trailing semi-colon"); |
|---|
| | 242 | else static if (A[0] == ';') |
|---|
| | 243 | const size_t scIndex = 0; |
|---|
| | 244 | else |
|---|
| | 245 | const size_t scIndex = 1 + scIndex!(A[1..$]); |
|---|
| | 246 | } |
|---|
| | 247 | // Look for "type symbols;" in A and return symbols as a comma separated list of names |
|---|
| | 248 | // (even if type is encountered more than once). Output may contain spaces and, if |
|---|
| | 249 | // non-empty, will contain a trailing comma. Assumes scIndex always returns less than A.$. |
|---|
| | 250 | template parseT(char[] type, char[] A) { |
|---|
| | 251 | static if (A.length < type.length + 1) // end of input, no match |
|---|
| | 252 | const char[] parseT = ""; |
|---|
| | 253 | else static if (A[0] == ' ') // leading whitespace: skip |
|---|
| | 254 | const char[] parseT = parseT!(type, A[1..$]); |
|---|
| | 255 | else static if (A[0..type.length] == type && A[type.length] == ' ') // match |
|---|
| | 256 | const char[] parseT = A[type.length+1 .. scIndex!(A)] ~ "," ~ |
|---|
| | 257 | parseT!(type, A[scIndex!(A)+1 .. $]); |
|---|
| | 258 | else // no match |
|---|
| | 259 | const char[] parseT = parseT!(type, A[scIndex!(A)+1 .. $]); |
|---|
| | 260 | } |
|---|
| | 261 | // May have a trailing comma. Assumes cIndex always returns less than A.$. |
|---|
| | 262 | template aaVars(char[] A) { |
|---|
| | 263 | static if (A.length == 0) |
|---|
| | 264 | const char[] aaVars = ""; |
|---|
| | 265 | else static if (A[0] == ' ') |
|---|
| | 266 | const char[] aaVars = aaVars!(A[1..$]); |
|---|
| | 267 | else |
|---|
| | 268 | const char[] aaVars = "\""~A[0..cIndex!(A)]~"\"[]:&"~A[0..cIndex!(A)] ~ "," ~ |
|---|
| | 269 | aaVars!(A[cIndex!(A)+1..$]); |
|---|
| | 270 | } |
|---|
| | 271 | // strip Trailing Comma |
|---|
| | 272 | template sTC(char[] A) { |
|---|
| | 273 | static if (A.length && A[$-1] == ',') |
|---|
| | 274 | const char[] sTC = A[0..$-1]; |
|---|
| | 275 | else |
|---|
| | 276 | const char[] sTC = A; |
|---|
| | 277 | } |
|---|
| | 278 | // if string is empty (other than space) return null, otherwise enclose: [A] |
|---|
| | 279 | template listOrNull(char[] A) { |
|---|
| | 280 | static if (A.length == 0) |
|---|
| | 281 | const char[] listOrNull = "null"; |
|---|
| | 282 | else static if (A[0] == ' ') |
|---|
| | 283 | const char[] listOrNull = listOrNull!(A[1..$]); |
|---|
| | 284 | else |
|---|
| | 285 | const char[] listOrNull = "["~A~"]"; |
|---|
| | 286 | } |
|---|
| | 287 | } protected { |
|---|
| | 288 | /** Produces the implementation code to go in the constuctor. */ |
|---|
| | 289 | template aaDefs(char[] A) { |
|---|
| | 290 | const char[] aaDefs = |
|---|
| | 291 | "optsBool = " ~ listOrNull!(sTC!(aaVars!(parseT!("bool" , A)))) ~ ";\n" ~ |
|---|
| | 292 | "optsInt = " ~ listOrNull!(sTC!(aaVars!(parseT!("int" , A)))) ~ ";\n" ~ |
|---|
| | 293 | "optsDouble = "~ listOrNull!(sTC!(aaVars!(parseT!("double", A)))) ~ ";\n" ~ |
|---|
| | 294 | "optsCharA = " ~ listOrNull!(sTC!(aaVars!(parseT!("char[]", A)))) ~ ";\n"; |
|---|
| | 295 | } |
|---|
| | 296 | /+/** Produces the implementation code to go in the static constuctor. */ |
|---|
| | 297 | template optClassAdd(char[] symb) { |
|---|
| | 298 | const char[] optClassAdd = symb ~ "=new "~classinfo(this).name~";\n";//Options.addOptionsClass("~symb~", );\n"; |
|---|
| | 299 | }+/ |
|---|
| | 300 | /** mixin impl("type symbol[, symbol[...]];[type symbol[...];][...]") |
|---|
| | 301 | * |
|---|
| | 302 | * E.g. |
|---|
| | 303 | * --- |
|---|
| | 304 | * mixin (impl ("bool a, b; int i;")); |
|---|
| | 305 | * --- |
|---|
| | 306 | * |
|---|
| | 307 | * In case this() needs to be customized, mixin(impl!(A)) is equivalent to: |
|---|
| | 308 | * --- |
|---|
| | 309 | * mixin (A~"\nthis(){\n"~aaDefs!(A)~"}"); |
|---|
| | 310 | * --- |
|---|
| | 311 | * |
|---|
| | 312 | * Notes: Only use space as whitespace (no new-lines or tabs). Make sure to add a trailing |
|---|
| | 313 | * semi-colon (;) or you'll get told off! :D |
|---|
| | 314 | * |
|---|
| | 315 | * In general errors aren't reported well. Trial with pragma (msg, impl!(...)); if |
|---|
| | 316 | * necessary. |
|---|
| | 317 | * |
|---|
| | 318 | * Extending: mixins could also be used for the static this() {...} or even the whole |
|---|
| | 319 | * class, but doing would rather decrease readability of any implementation. */ |
|---|
| | 320 | template impl(char[] A /+, char[] symb+/) { |
|---|
| | 321 | const char[] impl = A~"\nthis(){\n"~aaDefs!(A)~"}"; |
|---|
| | 322 | // ~"\nstatic this(){\n"~optClassAdd!(symb)~"}" |
|---|
| | 323 | } |
|---|
| | 324 | } |
|---|
| | 325 | /+/** mixin impl("type symbol[, symbol[...]];[type symbol[...];][...]") |
|---|
| | 326 | * |
|---|
| | 327 | * E.g. |
|---|
| | 328 | * --- |
|---|
| | 329 | * mixin (impl ("bool a, b; int i;")); |
|---|
| | 330 | * --- |
|---|
| | 331 | * The parser isn't highly accurate. */ |
|---|
| | 332 | // Try using templates instead? See std.metastrings |
|---|
| | 333 | static char[] impl (char[] A) { |
|---|
| | 334 | char[] bools; |
|---|
| | 335 | char[] ints; |
|---|
| | 336 | |
|---|
| | 337 | while (A.length) { |
|---|
| | 338 | // Trim whitespace |
|---|
| | 339 | { |
|---|
| | 340 | size_t i = 0; |
|---|
| | 341 | while (i < A.length && (A[i] == ' ' || (A[i] >= 9u && A[i] <= 0xD))) |
|---|
| | 342 | ++i; |
|---|
| | 343 | A = A[i..$]; |
|---|
| | 344 | } |
|---|
| | 345 | if (A.length == 0) break; |
|---|
| | 346 | |
|---|
| | 347 | char[] type; |
|---|
| | 348 | for (size_t i = 0; i < A.length; ++i) { |
|---|
| | 349 | if (A[i] == ' ' || (A[i] >= 9u && A[i] <= 0xD)) { |
|---|
| | 350 | type = A[0..i]; |
|---|
| | 351 | A = A[i+1..$]; |
|---|
| | 352 | break; |
|---|
| | 353 | } |
|---|
| | 354 | } |
|---|
| | 355 | |
|---|
| | 356 | char[] symbols; |
|---|
| | 357 | for (size_t i = 0; i < A.length; ++i) { |
|---|
| | 358 | if (A[i] == ';') { |
|---|
| | 359 | symbols = A[0..i]; |
|---|
| | 360 | A = A[i+1..$]; |
|---|
| | 361 | break; |
|---|
| | 362 | } |
|---|
| | 363 | } |
|---|
| | 364 | |
|---|
| | 365 | if (type == "bool") { |
|---|
| | 366 | if (bools.length) |
|---|
| | 367 | bools = bools ~ "," ~ symbols; |
|---|
| | 368 | else |
|---|
| | 369 | bools = symbols; |
|---|
| | 370 | } |
|---|
| | 371 | else if (type == "int") { |
|---|
| | 372 | if (ints.length) |
|---|
| | 373 | ints = ints ~ "," ~ symbols; |
|---|
| | 374 | else |
|---|
| | 375 | ints = symbols; |
|---|
| | 376 | } |
|---|
| | 377 | else { |
|---|
| | 378 | // Unfortunately, we cannot output non-const strings (even though func is compile-time) |
|---|
| | 379 | // We also cannot use pragma(msg) because the message gets printed even if the code isn't run. |
|---|
| | 380 | //pragma(msg, "Warning: impl failed to parse whole input string"); |
|---|
| | 381 | // Cannot use Cout / logger either. |
|---|
| | 382 | break; |
|---|
| | 383 | } |
|---|
| | 384 | } |
|---|
| | 385 | |
|---|
| | 386 | char[] ret; |
|---|
| | 387 | if (bools.length) |
|---|
| | 388 | ret = "bool "~bools~";\n"; |
|---|
| | 389 | if (ints.length) |
|---|
| | 390 | ret = ret ~ "int "~ints~";\n"; |
|---|
| | 391 | |
|---|
| | 392 | |
|---|
| | 393 | |
|---|
| | 394 | return ret; |
|---|
| | 395 | }+/ |
|---|