Mercurial > projects > mde
diff mde/file/mergetag/DefaultData.d @ 81:d8fccaa45d5f
Moved file IO code from mde/mergetag to mde/file[/mergetag] and changed how some errors are caught.
author | Diggory Hardy <diggory.hardy@gmail.com> |
---|---|
date | Fri, 29 Aug 2008 11:59:43 +0100 |
parents | mde/mergetag/DefaultData.d@61ea26abe4dd |
children | 01f4f5f1acc9 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mde/file/mergetag/DefaultData.d Fri Aug 29 11:59:43 2008 +0100 @@ -0,0 +1,181 @@ +/* LICENSE BLOCK +Part of mde: a Modular D game-oriented Engine +Copyright © 2007-2008 Diggory Hardy + +This program is free software: you can redistribute it and/or modify it under the terms +of the GNU General Public License as published by the Free Software Foundation, either +version 2 of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +/** This module contains the DefaultData class, and some notes possibly useful for implementing +* other types of DataSection. +*/ +module mde.file.mergetag.DefaultData; + +public import mde.file.mergetag.iface.IDataSection; +import mde.file.serialize; + + +/************************************************************************************************* + * Default DataSection class. + * + * Supported types are given by dataTypes. + * + * Currently DefaultData is only used for headers, and thus the list of supported types has been + * reduced to just those used in headers. Load order is HIGH_LOW, i.e. existing entries aren't + * overwritten. + *************************************************************************************************/ +/* The implementation now uses a fair bit of generic programming. Adjusting the types supported +* should be as simple as adjusting the list dataTypes, and possibly implemting new conversions in +* parseFrom and parseTo if you add new types (e.g. for cent or imaginary/complex types, or user types). +* +* There shouldn't really be any need to adjust the implementation, except perhaps to add new +* functions to the class (such as another type of output where the delegate used in writeAll isn't +* enough). +*/ +class DefaultData : IDataSection +{ + //BEGIN META + /* These functions are used to generate code. Compile-time functions rather than templates are + * used because they are easier to write and understand. Mixins are used to compile the resultant + * code. Must be declared before used since forward references aren't supported for compile-time + * functions. */ + + // Generate the correct name for each variable type. + static char[] varName (char[] type) { + char[] append = ""; + while (type.length >= 2 && type[$-2..$] == "[]") { + type = type[0..$-2]; + append ~= "A"; + } + return "_" ~ type ~ append; + } + + // Int-to-string converter, which may not be efficient but will run at compile time. + static char[] int2str (uint i) { + char[] ret; + const digits = "0123456789"; + if (i == 0) ret = "0"; + else for (; i > 0; i /= 10) ret = digits[i%10] ~ ret; + return ret; + } + + // Generate the code for variable declarations. + static char[] declerations (char[][] types) { + char[] ret = ""; + foreach (char[] type; types) ret ~= type ~ "[ID]\t" ~ varName(type) ~ ";\n"; + return ret; + } + + // Purely to add indentation. Could just return "" without affecting functionality. + static char[] indent (uint i) { + char[] ret = ""; + for (; i > 0; --i) ret ~= " "; + // This is not executable at compile time: + //ret.length = i * 4; // number of characters for each indentation + //ret[] = ' '; // character to indent with + return ret; + } + + /* Generates a binary search algorithm. + * + * Currently this is tailored to it's particular use (addTag). */ + static char[] binarySearch (char[] var, char[][] consts, int indents = 0) { + if (consts.length > 3) { + return indent(indents) ~ "if (" ~ var ~ " <= \"" ~ consts[$/2 - 1] ~ "\") {\n" ~ + binarySearch (var, consts[0 .. $/2], indents + 1) ~ + indent(indents) ~ "} else {\n" ~ + binarySearch (var, consts[$/2 .. $], indents + 1) ~ + indent(indents) ~ "}\n"; + } else { + char[] ret; + ret ~= indent(indents); + foreach (c; consts) { + ret ~= "if (" ~ var ~ " == \"" ~ c ~ "\") {\n" ~ + //indent(indents+1) ~ varName(c) ~ "[id] = parseTo!(" ~ c ~ ") (dt);\n" ~ + indent(indents+1) ~ "if ((id in "~varName(c)~") is null)\n" ~ + indent(indents+2) ~ varName(c)~"[id] = parseTo!(" ~ c ~ ") (dt);\n" ~ + indent(indents) ~ "} else "; + } + ret = ret[0..$-6] ~ '\n'; // remove last else + return ret; + } + } + + // Generates the code to write data members (writeAll). + static char[] writeVars () { + char[] code = ""; + foreach (i,type; dataTypes) { + code ~= "foreach (id, dt; " ~ varName(type) ~ ") itemdlg (dataTypes[" ~ int2str(i) ~ "], id, parseFrom!(" ~ type ~ ")(dt));\n"; + } + return code; + } + //END META + + /** Data Members + * + * These types are all stored directly, as below, are available for direct access. The variable + * names are created dynamically at compile-time based on the dataTypes list. + * ------------------ + * int[ID] _int; // name is type prefixed by _ + * char[][ID] _charA; // [] is replaced by A + * ------------------ + * + * An alternative access method is to use the provided templates: + * -------------------- + * template Arg(T) { + * alias Name Arg; + * } + * + * type y = Arg!(type).Arg; // example of use + * -------------------- + * Note: trying to use Arg!(type) to implicitly refer to Arg!(type).Arg causes compiler errors + * due to the "alias Name Arg;" statement actually being a mixin. + */ + /+ All types previously supported. Most of these weren't used. + const char[][] dataTypes = ["bool","bool[]", + "byte","byte[]", + "char","char[]","char[][]", + "double","double[]", + "float","float[]", + "int","int[]", + "long","long[]", + "real","real[]", + "short","short[]", + "ubyte","ubyte[]", + "uint","uint[]", + "ulong","ulong[]", + "ushort","ushort[]"]; + +/ + const char[][] dataTypes = ["char[]", "char[][]"]; + + mixin (declerations (dataTypes)); // Declare all the variables. + + void addTag (char[] type, ID id, char[] dt) { /// Supports all types listed in dataTypes. + mixin (binarySearch ("type", dataTypes)); + } + + void writeAll (ItemDelg itemdlg) { /// Supports all types listed in dataTypes. + mixin (writeVars ()); + } + + /* These make no attempt to check Arg is valid. + * But if the symbol doesn't exist the complier will throw an error anyway, e.g.: + * Error: identifier '_boolAA' is not defined + */ + template ArgName (T : T[]) { + const char[] ArgName = ArgName!(T)~`A`; + } + template ArgName (T) { + const char[] ArgName = `_`~T.stringof; + } + template Arg(T) { + mixin(`alias `~ArgName!(T)~` Arg;`); + } +}