Mercurial > projects > mde
diff mde/Options.d @ 50:f68ae1d667f9
Options: impl template & new OptionsFont class.
Options: impl template to ease creating Options sub-classes.
New OptionsFont class (currently only controls render mode and hinting).
committer: Diggory Hardy <diggory.hardy@gmail.com>
author | Diggory Hardy <diggory.hardy@gmail.com> |
---|---|
date | Sun, 01 Jun 2008 18:22:54 +0100 |
parents | 1530d9c04d4d |
children | f000d6cd0f74 |
line wrap: on
line diff
--- a/mde/Options.d Sat May 31 13:10:06 2008 +0100 +++ b/mde/Options.d Sun Jun 01 18:22:54 2008 +0100 @@ -44,7 +44,8 @@ * Each sub-class provides named variables for maximal-speed reading. Local sub-class references * should be used for reading variables, and via the addOptionsClass() hook will be loaded from * files during pre-init (init0 stage). Do not write changes directly to the subclasses or they will -* not be saved; use, for example, Options.setBool(...). +* not be saved; use, for example, Options.setBool(...). Use an example like OptionsMisc as a +* template for creating a new Options sub-class. * * Details: Options sub-classes hold associative arrays of pointers to all option variables, with a * char[] id. This list is used for saving, loading and to provide generic GUI options screens. The @@ -224,41 +225,174 @@ //END Static //BEGIN Templates - template store(A...) { - alias A a; - } - template decRecurse(char[] A, B...) { - static if (B.length) const char[] decRecurse = A ~ ", " ~ decRecurse!(B); - else const char[] decRecurse = A; - } - template decBool(A...) { - const char[] decBool = "bool " ~ decRecurse!(A) ~ ";\n"; - } - template decInt(A...) { - const char[] decInt = "int " ~ decRecurse!(A) ~ ";\n"; - } - template decDouble(A...) { - const char[] decDouble = "double " ~ decRecurse!(A) ~ ";\n"; + private { + // Return index of first comma, or halts if not found. + template cIndex(char[] A) { + static if (A.length == 0) + static assert (false, "Error in implementation"); + else static if (A[0] == ',') + const size_t cIndex = 0; + else + const size_t cIndex = 1 + cIndex!(A[1..$]); + } + // Return index of first semi-colon, or halts if not found. + template scIndex(char[] A) { + static if (A.length == 0) + static assert (false, "Error: no trailing semi-colon"); + else static if (A[0] == ';') + const size_t scIndex = 0; + else + const size_t scIndex = 1 + scIndex!(A[1..$]); + } + // Look for "type symbols;" in A and return symbols as a comma separated list of names + // (even if type is encountered more than once). Output may contain spaces and, if + // non-empty, will contain a trailing comma. Assumes scIndex always returns less than A.$. + template parseT(char[] type, char[] A) { + static if (A.length < type.length + 1) // end of input, no match + const char[] parseT = ""; + else static if (A[0] == ' ') // leading whitespace: skip + const char[] parseT = parseT!(type, A[1..$]); + else static if (A[0..type.length] == type && A[type.length] == ' ') // match + const char[] parseT = A[type.length+1 .. scIndex!(A)] ~ "," ~ + parseT!(type, A[scIndex!(A)+1 .. $]); + else // no match + const char[] parseT = parseT!(type, A[scIndex!(A)+1 .. $]); + } + // May have a trailing comma. Assumes cIndex always returns less than A.$. + template aaVars(char[] A) { + static if (A.length == 0) + const char[] aaVars = ""; + else static if (A[0] == ' ') + const char[] aaVars = aaVars!(A[1..$]); + else + const char[] aaVars = "\""~A[0..cIndex!(A)]~"\"[]:&"~A[0..cIndex!(A)] ~ "," ~ + aaVars!(A[cIndex!(A)+1..$]); + } + // strip Trailing Comma + template sTC(char[] A) { + static if (A.length && A[$-1] == ',') + const char[] sTC = A[0..$-1]; + else + const char[] sTC = A; + } + // if string is empty (other than space) return null, otherwise enclose: [A] + template listOrNull(char[] A) { + static if (A.length == 0) + const char[] listOrNull = "null"; + else static if (A[0] == ' ') + const char[] listOrNull = listOrNull!(A[1..$]); + else + const char[] listOrNull = "["~A~"]"; + } + } protected { + /** Produces the implementation code to go in the constuctor. */ + template aaDefs(char[] A) { + const char[] aaDefs = + "optsBool = " ~ listOrNull!(sTC!(aaVars!(parseT!("bool" , A)))) ~ ";\n" ~ + "optsInt = " ~ listOrNull!(sTC!(aaVars!(parseT!("int" , A)))) ~ ";\n" ~ + "optsDouble = "~ listOrNull!(sTC!(aaVars!(parseT!("double", A)))) ~ ";\n" ~ + "optsCharA = " ~ listOrNull!(sTC!(aaVars!(parseT!("char[]", A)))) ~ ";\n"; + } + /+/** Produces the implementation code to go in the static constuctor. */ + template optClassAdd(char[] symb) { + const char[] optClassAdd = symb ~ "=new "~classinfo(this).name~";\n";//Options.addOptionsClass("~symb~", );\n"; + }+/ + /** mixin impl("type symbol[, symbol[...]];[type symbol[...];][...]") + * + * E.g. + * --- + * mixin (impl ("bool a, b; int i;")); + * --- + * + * In case this() needs to be customized, mixin(impl!(A)) is equivalent to: + * --- + * mixin (A~"\nthis(){\n"~aaDefs!(A)~"}"); + * --- + * + * Notes: Only use space as whitespace (no new-lines or tabs). Make sure to add a trailing + * semi-colon (;) or you'll get told off! :D + * + * In general errors aren't reported well. Trial with pragma (msg, impl!(...)); if + * necessary. + * + * Extending: mixins could also be used for the static this() {...} or even the whole + * class, but doing would rather decrease readability of any implementation. */ + template impl(char[] A /+, char[] symb+/) { + const char[] impl = A~"\nthis(){\n"~aaDefs!(A)~"}"; + // ~"\nstatic this(){\n"~optClassAdd!(symb)~"}" + } } - template decCharA(A...) { - const char[] decCharA = "char[] " ~ decRecurse!(A) ~ ";\n"; - } - template aaRecurse(char[] A, B...) { - static if (B.length) const char[] aaRecurse = "\""~A~"\"[]:&"~A ~ ", " ~ aaRecurse!(B); - else const char[] aaRecurse = "\""~A~"\"[]:&"~A; - } - template aaBool(A...) { - const char[] aaBool = "optsBool = [" ~ aaRecurse!(A) ~ "];\n"; - } - template aaInt(A...) { - const char[] aaInt = "optsInt = [" ~ aaRecurse!(A) ~ "];\n"; - } - template aaDouble(A...) { - const char[] aaDouble = "optsDouble = [" ~ aaRecurse!(A) ~ "];\n"; - } - template aaCharA(A...) { - const char[] aaCharA = "optsCharA = [" ~ aaRecurse!(A) ~ "];\n"; - } + /+/** mixin impl("type symbol[, symbol[...]];[type symbol[...];][...]") + * + * E.g. + * --- + * mixin (impl ("bool a, b; int i;")); + * --- + * The parser isn't highly accurate. */ + // Try using templates instead? See std.metastrings + static char[] impl (char[] A) { + char[] bools; + char[] ints; + + while (A.length) { + // Trim whitespace + { + size_t i = 0; + while (i < A.length && (A[i] == ' ' || (A[i] >= 9u && A[i] <= 0xD))) + ++i; + A = A[i..$]; + } + if (A.length == 0) break; + + char[] type; + for (size_t i = 0; i < A.length; ++i) { + if (A[i] == ' ' || (A[i] >= 9u && A[i] <= 0xD)) { + type = A[0..i]; + A = A[i+1..$]; + break; + } + } + + char[] symbols; + for (size_t i = 0; i < A.length; ++i) { + if (A[i] == ';') { + symbols = A[0..i]; + A = A[i+1..$]; + break; + } + } + + if (type == "bool") { + if (bools.length) + bools = bools ~ "," ~ symbols; + else + bools = symbols; + } + else if (type == "int") { + if (ints.length) + ints = ints ~ "," ~ symbols; + else + ints = symbols; + } + else { + // Unfortunately, we cannot output non-const strings (even though func is compile-time) + // We also cannot use pragma(msg) because the message gets printed even if the code isn't run. + //pragma(msg, "Warning: impl failed to parse whole input string"); + // Cannot use Cout / logger either. + break; + } + } + + char[] ret; + if (bools.length) + ret = "bool "~bools~";\n"; + if (ints.length) + ret = ret ~ "int "~ints~";\n"; + + + + return ret; + }+/ //END Templates } @@ -341,22 +475,7 @@ /** A home for all miscellaneous options, at least for now. */ OptionsMisc miscOpts; class OptionsMisc : Options { - alias store!("useThreads", "exitImmediately") BOOL; - alias store!("logLevel") INT; - alias store!("pollInterval") DOUBLE; - alias store!("L10n") CHARA; - - mixin (decBool!(BOOL.a)); - mixin (decInt!(INT.a)); - mixin (decDouble!(DOUBLE.a)); - mixin (decCharA!(CHARA.a)); - - this () { - mixin (aaBool!(BOOL.a)); - mixin (aaInt!(INT.a)); - mixin (aaDouble!(DOUBLE.a)); - mixin (aaCharA!(CHARA.a)); - } + mixin (impl!("bool useThreads, exitImmediately; int logLevel; double pollInterval; char[] L10n;")); static this() { miscOpts = new OptionsMisc;