Changeset 8:f63f4f41a2dc
- Timestamp:
- 01/25/08 13:17:38 (1 year ago)
- Files:
-
- conf/input.mtt (modified) (1 diff)
- doc/policies.txt (modified) (3 diffs)
- dsss.conf (modified) (1 diff)
- mde/events.d (modified) (2 diffs)
- mde/exception.d (modified) (1 diff)
- mde/init.d (modified) (5 diffs)
- mde/input/config.d (modified) (7 diffs)
- mde/input/core.d.old (deleted)
- mde/input/eventstream.d.old (deleted)
- mde/input/exception.d (modified) (1 diff)
- mde/input/input.d (modified) (6 diffs)
- mde/mde.d (modified) (3 diffs)
- mde/mergetag/dataset.d (modified) (4 diffs)
- mde/mergetag/doc/file-format-text.txt (modified) (7 diffs)
- mde/mergetag/doc/issues.txt (modified) (1 diff)
- mde/mergetag/exception.d (modified) (2 diffs)
- mde/mergetag/old-code/dataset.d.old (deleted)
- mde/mergetag/old-code/typeSpec.d.old (deleted)
- mde/mergetag/read.d (modified) (12 diffs)
- mde/mergetag/write.d (modified) (6 diffs)
- mde/test.d (modified) (2 diffs)
- mde/text/format.d (modified) (1 diff)
- mde/text/parse.d (modified) (1 diff)
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
conf/input.mtt
r7 r8 1 1 {MT01} 2 !< uint[]|0=[1]>3 { 0}4 <uint[][uint]| 0=[0x20000000 : [0x100, 3] ]>2 !<string[]|Configs=["Std"]> 3 {Default} 4 <uint[][uint]|B=[0x20000000 : [0x100, 3] ]> doc/policies.txt
r4 r8 6 6 Coding conventions: Mostly stick to those provided in the D specification. Generally indent with four spaces and use tabs to align comments. Aim to break long lines at around 100 chars (particularly with documentation); this isn't essential but provides a good guide and keeps text looking reasonable. With code, however, breaking lines doesn't always produce better-looking code. 7 7 8 Spelling: Use british or american (or other) spellings as you like, but BE CONSISTANT at least for code symbols within packages. For text output to the user there need be no convention until internationalisation support is built-in. As far as internationalisation/localisation is concerned, does it make sense to translate log messages or not? (They are going to be seen by end-users, but will largely be used by developers.) 8 Identifiers: as for the D spec, use descriptive words for identifiers, although try to keep them from being overlong. Use capital letters to show separate words, not _s. E.g.: file, readFile; not: rdFile, read_file, readFileUsingMyMethodNow. Again, this is only really a guideline. 9 10 Module/file names: unless you have a good reason, keep names all lower-case. And if you're programming on windows, make sure you always use the correct capitalisation (yeah, of course...). 11 12 Spelling: Use british or american (or other) spellings as you like, but BE CONSISTANT at least for code symbols within packages (i.e. if you write your own package you choose the spelling, and for comments it doesn't matter, unless it's actually refering to a symbol). For text output to the user there need be no convention until internationalisation support is built-in. As far as internationalisation/localisation is concerned, does it make sense to translate log messages or not? (They are going to be seen by end-users, but will largely be used by developers.) 9 13 10 14 11 15 Package design principle: use a separate package for each module of the engine. In most packages where there is only one module (file) imported by other parts of the engine, that module should have the same name as the package and be designed to have a standardised interface to the package so that the package could be replaced with another as a drop-in replacement (written with the same interface). Of course in many cases it may not be possible to swich one package for another quite this easily, but holding to this principle should at least minimise the amount of work necessary when doing so. 16 17 18 Module imports: modules should publically import dependancy modules likely to be needed by dependant modules, in particular the package-level exception module (where used & use by dependants is expected). 12 19 13 20 … … 18 25 19 26 In general the levels should be used as follows: 20 Trace Where required or thought highly useful for debugging 21 Info Sparingly, for informational purposes (e.g. when parsing a file). Should not generally be used repetitively (within loops, etc.). 22 Warn For small errors which can be overlooked, even if they MAY cause bigger problems later. 27 Trace Where required or thought highly useful for debugging, and only compiled in debugging mode. 28 Info Sparingly, for informational purposes (e.g. when parsing a file). Should not generally be used repetitively (within loops, etc.). Not for reporting unexpected behaviour. 29 Warn For small errors which can be overlooked, even if they MAY cause bigger problems later. I.e. something unexpected, but not necessarily a major problem, happens. 23 30 Error For errors which directly: 24 31 ⢠cut-short a (reasonably large) operation (e.g. reading a file). … … 26 33 Fatal For errors directly (i.e. definately and almost immediately) ending the program. 27 34 35 For all levels bar trace, messages should if possible be understandable to end users, while (for warn and above) including enough information to fix the problem, including code symbols if necessary. 36 Thus: 37 Trace output should only be available when compiled in debug mode. 38 When run by an end-user (with info-level logging enabled), 39 ⢠info messages normally occur and should be understandable to end users; 40 ⢠warn messages may occur, and may or may not indicate problems; 41 ⢠error messages indicate that something big is wrong, and if the program still runs it is unlikely to be usable as intended; 42 ⢠fatal messages indicate a problem preventing the program from running. 43 28 44 29 Thrown errors should use an exception class specific to at least the package involved to enable specific catching of errors. Exception classes should be defined within a module exception.d in the package directory. Exception classes should all contain a this() CTOR and possibly a this(char[] msg) CTOR. 45 46 Thrown errors should use an exception class specific to at least the package involved to enable specific catching of errors. Exception classes should be defined within a module exception.d in the package directory. Exception classes should generally follow the conventions within mde/exception.d to aid in providing reasonable error messages. dsss.conf
r4 r8 4 4 #[mde/mergetag] 5 5 #[mde/input] 6 [test/MTTest.d]7 target=bin/MTTest6 #[test/MTTest.d] 7 #target=bin/MTTest 8 8 mde/events.d
r4 r8 8 8 import derelict.sdl.events; 9 9 10 import tango.util.log.Log : Log, Logger; 11 12 private Logger logger; 13 static this() { 14 logger = Log.getLogger ("mde.events"); 15 } 16 10 17 static bool run = true; 11 18 12 static this() {19 void addEventsSchedule () { 13 20 Scheduler.perFrame (&pollEvents); 14 21 } … … 19 26 switch (event.type) { 20 27 case SDL_QUIT: 28 logger.info ("Quit requested"); 21 29 run = false; 22 break;30 break; 23 31 default: 24 Input.instance()(event);32 input (event); 25 33 } 26 34 } mde/exception.d
r7 r8 26 26 } 27 27 } 28 29 class DynamicLibraryLoadException : initException {30 this (char[] msg) {31 super("when loading dynamic library: " ~ msg);32 }33 }mde/init.d
r6 r8 6 6 module mde.init; 7 7 8 import mde.exception;8 public import mde.exception; 9 9 10 import mde.events; 10 11 import mde.input.input; 12 import mde.input.joystick; 11 13 12 14 // tango imports … … 14 16 import tango.util.log.Log : Log, Logger; 15 17 import tango.util.log.ConsoleAppender : ConsoleAppender; 18 import tango.stdc.stringz : fromUtf8z; 16 19 17 20 import derelict.sdl.sdl; … … 43 46 scope class Init 44 47 { 45 static Logger logger;48 private static Logger logger; 46 49 static this() { 47 50 logger = Log.getLogger ("mde.init.Init"); … … 52 55 * Runs general initialisation code, in a threaded manner where this isn't difficult. 53 56 * 54 * If this fails by throwing an exception, it must run necessary cleanup first since the DTOR 55 * cannot be run. */ 57 * If any init fails, it must run necessary cleanup first since the DTOR cannot be run. */ 56 58 /* In a single-threaded function this could be done with: 57 59 * scope(failure) cleanup; … … 63 65 this() 64 66 { 67 /* Initialisation functions. 68 * 69 * These should each handle a separate area of initialisation such that these functions could 70 * be run simultaneously in separate threads. */ 71 72 bool initFailure = false; // Set true if something goes wrong and we need to abort. 73 void setFailure () { // For synchronization, although may be unnecessary 74 synchronized initFailure = true; 75 } 76 void delegate() [] initFuncs = [ 77 delegate void() { 78 // Inits SDL and related stuff (joystick). 79 try { 80 // SDL Joystick, used by mde.input 81 DerelictSDL.load(); 82 } catch (DerelictException de) { 83 logger.fatal ("Loading dynamic library failed:"); 84 logger.fatal (de.msg); 85 86 setFailure (); // abort 87 return; 88 } 89 logger.info ("Derelict: loaded SDL"); 90 91 if (SDL_Init (SDL_INIT_VIDEO | SDL_INIT_JOYSTICK)) { 92 logger.fatal ("SDL initialisation failed:"); 93 char* msg = SDL_GetError (); 94 logger.fatal (msg ? fromUtf8z(msg) : "no reason available"); 95 96 setFailure (); // abort 97 return; 98 } 99 100 SDL_SetVideoMode (800, 600, 32, 0); 101 102 addCleanupFct (&cleanupSDL); 103 logger.info ("SDL initialised"); 104 105 openJoysticks (); // after SDL init 106 addCleanupFct (&closeJoysticks); 107 } 108 ]; 109 65 110 // Start all threads 66 ThreadGroup tg = new ThreadGroup; 67 tg.create(&initSDL); 111 ThreadGroup tg = new ThreadGroup; // can't fail since it does nothing (tango 0.99.4) (unless out of memory â anyway should be safe to throw at this point) 112 try { // creating/starting threads can fail 113 foreach (func; initFuncs) tg.create(func); 114 } catch (Exception e) { // should be a ThreadException, but catch all Exceptions 115 logger.warn ("Caught exception while trying to create threads:"); 116 logger.warn (e.msg); 117 logger.warn ("Will continue in a non-threaded manner."); 118 119 foreach (func; initFuncs) func(); 120 } 68 121 69 122 // Do some initialisation in the main thread 70 Input.instance.loadConfig (0); 123 input.loadConfig (); // (may also create instance) 124 addEventsSchedule (); 71 125 72 // Wait for all threads to complete 73 try { 74 tg.joinAll (true); // rethrows any exceptions 126 // Wait for all threads to complete. 127 // If something went wrong, we still need to do this before cleaning up. 128 foreach (t; tg) { 129 try { 130 t.join (true); 131 /+ Will only catch thread exceptions; but even so something still went badly wrong so we want the same functionality. 132 } catch (ThreadException e) { // Any threading exception 133 +/ 134 } catch (Exception e) { // Any other exception, i.e. caught from thread. 135 // Relying on catching exceptions thrown by other threads is a bad idea. 136 // Hence all threads should catch their own exceptions and return safely. 137 138 logger.fatal ("Exception caught during init:"); 139 logger.fatal (e.msg); 140 logger.fatal ("Please report this; it should NEVER happen."); 141 142 setFailure (); // abort (but join other threads first) 143 } 75 144 } 76 catch (initException e) { // Any problems? 145 146 if (initFailure) { 77 147 // All cleanup-on-failure must be done here. 78 148 runCleanupFcts(); 79 throw e; // Rethrow. Warning: if multiple threads throw exceptions, only one gets returned. 149 150 // Throw an exception to signal failure and prevent DTOR from also running. 151 throw new initException ("Initialisation failed due to above exceptions."); 80 152 } 81 82 153 } 83 154 84 /* Initialisation functions.155 /** DTOR - runs cleanup. 85 156 * 86 * These should each handle a separate area of initialisation such that these functions could 87 * be run simultaneously in separate threads. */ 88 void initSDL () { 89 try { 90 // SDL Joystick, used by mde.input 91 DerelictSDL.load(); 92 } catch (DerelictException de) { 93 throw new DynamicLibraryLoadException (de.msg); 94 } 95 logger.info ("Derelict: loaded SDL"); 96 97 SDL_Init (SDL_INIT_TIMER | SDL_INIT_JOYSTICK); 98 addCleanupFct (&cleanupSDL); 99 logger.info ("SDL initialised"); 100 } 101 157 * Currently unthreaded; probably might as well stay that way. */ 102 158 ~this() 103 159 { 104 runCleanupFcts(); 160 runCleanupFcts(); // if threading, note not all functions can be called simultaeneously 105 161 } 106 162 mde/input/config.d
r7 r8 4 4 debug import mde.text.format; 5 5 6 import mde.input.exception;6 public import mde.input.exception; 7 7 8 import mde.mergetag.read;8 import MT = mde.mergetag.read; 9 9 import mde.text.parse; 10 10 … … 12 12 import tango.util.collection.TreeBag : TreeBag; 13 13 14 Logger logger;14 private Logger logger; 15 15 static this() { 16 16 logger = Log.getLogger ("mde.input.config"); … … 22 22 * Class extends DataSection so that it can be loaded by mergetag easily. 23 23 */ 24 class Config : DataSection24 class Config : MT.DataSection 25 25 { 26 26 alias uint[] outQueue; // This is the type for the out queue config data. … … 87 87 uint[] inheritants; /// Other profiles to inherit. 88 88 89 // FIXME: using uint IDs really isn't nice... 90 static Config[uint] configs; /// All configs loaded by load(). 89 static Config[char[]] configs; /// All configs loaded by load(). 91 90 private static TreeBag!(char[]) loadedFiles; // all filenames load tried to read 92 91 … … 100 99 if (loadedFiles.contains (filename)) return; // forget it; already done that 101 100 loadedFiles.add (filename); 102 Reader file;101 MT.Reader file; 103 102 try { 104 file = new Reader(filename, null, true); // open and read header103 file = new MT.Reader(filename, null, true); // open and read header 105 104 // TODO: also load user-config file 106 105 107 106 file.dataSecCreator = 108 function DataSection (ID) { return new Config; };107 function MT.DataSection (MT.ID) { return new Config; }; 109 108 110 enum : ID { CONFIGS } 111 ID[] configs; // active config sections (may not exist) 112 uint[]* configs_p = CONFIGS in file.dataset.header._uintA; 109 // D2.0: enum MT.ID CONFIGS = "Configs"; 110 const MT.ID CONFIGS = cast(MT.ID)"Configs"; 111 MT.ID[] configs; // active config sections (may not exist) 112 MT.ID[]* configs_p = cast(MT.ID[]*) (CONFIGS in file.dataset.header._stringA); 113 113 114 if (configs_p) file.read( cast(ID[])*configs_p); // restrict to this set IF a restriction was given114 if (configs_p) file.read(*configs_p); // restrict to this set IF a restriction was given 115 115 else file.read(); // otherwise read all 116 116 } 117 catch (MT Exception) {117 catch (MT.MTException) { 118 118 logger.error ("Unable to load configs from: " ~ filename); 119 119 throw new ConfigLoadException; 120 120 } 121 // FIXME: don't override configs if not empty 122 configs = cast (Config[uint]) file.dataset.sec; 123 // NOTE: this is in some ways dangerous (assuming all DataSections are Configs), but they should be. 121 122 // NOTE: It is in some ways a bad idea assuming all DataSections are Configs, but they should be. 123 if (!configs) { // these are the first Configs loaded 124 configs = cast (Config[char[]]) file.dataset.sec; 125 } else { // add to existing Configs, replacing ones with same ID 126 foreach (i, sec; file.dataset.sec) { 127 Config c = cast(Config) sec; 128 if (c) configs[i] = c; // Check, because we don't want null entries in configs 129 else debug logger.warn ("Ended up with DataSection of wrong type; this should never happen."); 130 } 131 } 132 124 133 debug { 125 134 char tmp[128] = void; 126 logger. info(logger.format (tmp, "Loaded {} config sections.", configs.length));135 logger.trace (logger.format (tmp, "Loaded {} config sections.", configs.length)); 127 136 foreach (id, cfg; configs) { 128 137 logger.trace ("Section "~format!(uint)(id)~": " ~ format!(uint[][uint])(cfg.button) ~ " (" ~ format!(uint)(cfg.dnbc) ~ ")"); … … 131 140 } 132 141 133 private enum QUEUE : ID { BUTTON, AXIS, MOUSE } 142 // D2.0: private enum QUEUE : MT.ID { BUTTON = "B", AXIS = "A", MOUSE = "M" } 143 private struct QUEUE { 144 static const MT.ID BUTTON = cast(MT.ID)"B", AXIS = cast(MT.ID)"A", MOUSE = cast(MT.ID)"M"; 145 } 134 146 private this() {} // Private since this class should only be created from here. 135 147 136 void addTag (char[] tp, ID id, char[] dt) {148 void addTag (char[] tp, MT.ID id, char[] dt) { 137 149 if (tp == "uint[][uint]") { 138 150 if (id == QUEUE.BUTTON) { … … 143 155 else if (id == QUEUE.AXIS) axis = cast(outQueue[uint]) parse!(uint[][uint]) (dt); 144 156 else if (id == QUEUE.MOUSE) mouse = cast(outQueue[uint]) parse!(uint[][uint]) (dt); 145 else { 146 char[80] tmp; 147 logger.info (logger.format(tmp, "Unexpected tag encountered with ID {}", id)); 148 } 157 else logger.warn ("Unexpected tag encountered with ID " ~ cast(char[])id); 149 158 } // FIXME: add support for name and inheritants. 150 else throw new MT UnknownTypeException ("Input Config: only uint[][uint] type supported");159 else throw new MT.MTUnknownTypeException ("Input Config: only uint[][uint] type supported"); 151 160 } 152 161 void writeAll (ItemDelg) { mde/input/exception.d
r7 r8 1 1 module mde.input.exception; 2 2 3 import mde.exception;3 public import mde.exception; 4 4 5 5 /// Base Input exception class. mde/input/input.d
r7 r8 7 7 // package imports 8 8 import mde.input.config; 9 import mde.input.exception;9 public import mde.input.exception; 10 10 11 11 // sdl imports 12 12 import derelict.sdl.events; 13 13 14 import tango.util.log.Log : Log, Logger; 15 16 Input input; /// Global instance of input. 17 private Logger logger; 18 19 static this() { 20 input = new Input; 21 22 logger = Log.getLogger ("mde.input.config"); 23 } 24 25 /// Class encapsulating all input functionality. 14 26 class Input 15 27 { … … 19 31 alias void delegate(inputID, real) AxisCallback; 20 32 alias void delegate(inputID, real,real) MouseCallback; 21 22 /// Stores a static instance of Input for most usage.23 static Input instance () {24 static Input instance;25 26 if (instance is null) instance = new Input;27 return instance;28 }29 33 30 34 /** Get key status at this ID. … … 104 108 case SDL_JOYBUTTONDOWN: 105 109 case SDL_JOYBUTTONUP: 110 debug { 111 char tmp[128] = void; 112 logger.trace (logger.format (tmp, "Got a joystick button event: ({}, {}) - {}", event.jbutton.which, event.jbutton.button, event.jbutton.state)); 113 } 106 114 outQueue* p = (Config.B.JOYBUTTON | (event.jbutton.which << 12) | event.jbutton.button) in config.button; 107 115 if (p) bEventOut (event.jbutton.state == 0x1, readOutQueue(*p)); 108 break;116 break; 109 117 110 118 /+ … … 121 129 +/ 122 130 default: 123 return false;131 return false; 124 132 } 125 133 return true; … … 128 136 /** Resets relative movement of mice / joystick balls to zero. 129 137 * 130 * Should probably be called once-per-frame if these are used. 138 * Should be called once-per-frame if these are used, but must be called after their state has 139 * been read (e.g. just before updating the input). 131 140 */ 132 141 void frameReset () { … … 140 149 * Returns: true if the requested config id wasn't found. 141 150 */ 142 bool loadConfig ( uint id) {151 bool loadConfig () { 143 152 Config.load("conf/input.mtt"); // FIXME: filename 144 Config* c_p = idin Config.configs;153 Config* c_p = "Default" in Config.configs; 145 154 if (c_p) { 146 155 config = *c_p; 147 156 return false; 148 logger.info ("Succesfully loaded config."); 149 } 157 debug logger.trace ("Succesfully loaded config."); 158 } 159 debug logger.warn ("Config \"Default\" not found."); 150 160 return true; 151 161 } mde/mde.d
r4 r8 12 12 13 13 import mde.input.input; 14 15 import mde.mergetag.read;16 14 17 15 // External library imports … … 37 35 } 38 36 39 Input input = Input.instance();40 37 input.addButtonCallback (cast(Input.inputID) 3u, delegate void(Input.inputID i, bool b) { 41 38 Stdout ("Event: ")(i)(" changed to: ")(b).newline; … … 43 40 bool oldb = false; 44 41 45 /+while (run)+/46 for (ulong t = 0; t < 100; ++t){42 while (run) 43 /+for (ulong t = 0; t < 100; ++t)+/ { 47 44 Scheduler.run (Clock.now()); 48 45 mde/mergetag/dataset.d
r7 r8 1 /// This module contains a minimal definition of a MergeTag DataSet.1 /// This module contains the mergetag DataSet class together with an interface for DataSections. 2 2 module mde.mergetag.dataset; 3 3 4 4 // package imports 5 import mde.mergetag.exception; 5 public import mde.mergetag.exception; 6 public import mde.mergetag.defaultdata; // used in DataSet so it should be publically imported 6 7 7 // other mde imports 8 import mde.text.util; 9 import mde.text.parse : parse; 10 import mde.text.format : format; 11 12 // tango imports 13 import Util = tango.text.Util; 14 import tango.util.log.Log : Log, Logger; 8 //import tango.util.log.Log : Log, Logger; 15 9 16 10 /** Typedef for data & section indexes (can be changed to ulong if necessary.) */ 17 typedef uintID;11 typedef char[] ID; 18 12 19 13 package struct MTFormatVersion { … … 35 29 } 36 30 37 private Logger logger;31 /+private Logger logger; 38 32 static this () { 39 33 logger = Log.getLogger ("mde.mergetag.dataset"); 40 } 41 42 struct TextTag { 43 TextTag opCall (char[] _tp, ID _id, char[] _dt) { 44 TextTag ret; 45 ret.tp = _tp; 46 ret.id = _id; 47 ret.dt = _dt; 48 return ret; 49 } 50 char[] tp, dt; 51 ID id; 52 } 34 }+/ 53 35 54 36 /************************************************************************************************** 55 37 * Data class; contains a DataSection class instance for each loaded section of a file. 38 * 39 * Stored data is available for direct access via header and sec; all functions are just helper 40 * functions. 56 41 * 57 42 * Any class implementing DataSection may be used to store data; by default a DefaultData class is … … 63 48 class DataSet 64 49 { 65 DefaultData header; /// Header 50 DefaultData header; /// Header section. 66 51 DataSection[ID] sec; /// Dynamic array of sections 67 68 /// Return a reference to the indexed item69 DataSection opIndex(ID i) {70 return sec[i];71 }72 52 73 53 /// Template to return all sections of a child-class type. … … 114 94 } 115 95 116 /**117 * Default DataSection class.118 *119 * Supports all the basic types currently supported and array versions of120 * each (except no arrays of binary or string types; these are already arrays).121 * Doesn't support custom types, but inheriting classes may add support.122 */123 /* Note: I wrote this comment when the code looked rather worse. It's still partially applicable though.124 *125 * Due to a failure to use generic programming techniques for most of this (maybe because it's not126 * possible or maybe just because I don't know how to use templates properly) a lot of this code is127 * really horrible and has to refer to EVERY data member.128 * Be really careful if you add any items to this class.129 *130 * I really don't like having to do things like this, but it provides a lot of benefits such as no131 * need to store types and no need to check an argument's type for every access (this could be done132 * by throwing errors, but then an incorrect (perhaps hand-edited) data file could cause a lot of133 * errors to be thrown).134 */135 class DefaultData : DataSection136 {137 //BEGIN DATA138 /** Data Members139 *140 * These names are available for direct access.141 *142 * An alternative access method is to use the provided templates:143 * --------------------144 * template Arg(T) {145 * alias Name Arg;146 * }147 * --------------------148 *149 * Use with a mixin or directly:150 * --------------------151 * mixin Arg!(type);152 * auto x = Arg;153 *154 * type y = Arg!(type).Arg;155 * --------------------156 * Note: trying to use Arg!(type) to implicitly refer to Arg!(type).Arg causes compiler errors due to157 * --- alias Name Arg; ---158 * actually being a mixin.159 */160 161 bool [ID] _bool;162 byte [ID] _byte; /// ditto163 short [ID] _short; /// ditto164 int [ID] _int; /// ditto165 long [ID] _long; /// ditto166 ubyte [ID] _ubyte; /// ditto167 ushort [ID] _ushort; /// ditto168 uint [ID] _uint; /// ditto169 ulong [ID] _ulong; /// ditto170 171 char [ID] _char; /// ditto172 173 float [ID] _float; /// ditto174 double [ID] _double; /// ditto175 real [ID] _real; /// ditto176 177 bool[] [ID] _boolA; /// ditto178 byte[] [ID] _byteA; /// ditto179 short[] [ID] _shortA; /// ditto180 int[] [ID] _intA; /// ditto181 long[] [ID] _longA; /// ditto182 ubyte[] [ID] _ubyteA; /// ditto183 ushort[] [ID] _ushortA; /// ditto184 uint[] [ID] _uintA; /// ditto185 ulong[] [ID] _ulongA; /// ditto186 187 char[] [ID] _charA; /// ditto188 189 float[] [ID] _floatA; /// ditto190 double[] [ID] _doubleA; /// ditto191 real[] [ID] _realA; /// ditto192 193 /** Alias names */194 alias _ubyteA _binary;195 alias _charA _string; /// ditto196 //END DATA197 198 void addTag (char[] tp, ID id, char[] dt) { /// Supports all standard types.199 if (tp.length == 0) throw new MTUnknownTypeException;200 // split list up a bit for performance:201 if (tp[0] < 'l') {202 if (tp[0] < 'd') {203 mixin ( `if (tp == "binary") addTag_add!(ubyte[]) (id, dt);`204 ~ addTag_elifIsType_add!(bool)205 ~ addTag_elifIsType_add!(bool[])206 ~ addTag_elifIsType_add!(byte)207 ~ addTag_elifIsType_add!(byte[])208 ~ addTag_elifIsType_add!(char)209 ~ addTag_elifIsType_add!(char[])210 ~ `else throw new MTUnknownTypeException;` );211 } else {212 mixin ( `if (tp == "double") addTag_add!(double) (id, dt);`213 ~ addTag_elifIsType_add!(double[])214 ~ addTag_elifIsType_add!(float)215 ~ addTag_elifIsType_add!(float[])216 ~ addTag_elifIsType_add!(int)217 ~ addTag_elifIsType_add!(int[])218 ~ `else throw new MTUnknownTypeException;` );219 }220 } else {221 if (tp[0] < 'u') {222 mixin ( `if (tp == "long") addTag_add!(long) (id, dt);`223 ~ addTag_elifIsType_add!(long[])224 ~ addTag_elifIsType_add!(real)225 ~ addTag_elifIsType_add!(real[])226 ~ addTag_elifIsType_add!(short)227 ~ addTag_elifIsType_add!(short[])228 ~ `else if (tp == "string") addTag_add!(char[]) (id, dt);`229 ~ `else throw new MTUnknownTypeException;` );230 } else {231 mixin ( `if (tp == "ubyte") addTag_add!(ubyte) (id, dt);`232 ~ addTag_elifIsType_add!(ubyte[])233 ~ addTag_elifIsType_add!(ushort)234 ~ addTag_elifIsType_add!(ushort[])235 ~ addTag_elifIsType_add!(uint)236 ~ addTag_elifIsType_add!(uint[])237 ~ addTag_elifIsType_add!(ulong)238 ~ addTag_elifIsType_add!(ulong[])239 ~ `else throw new MTUnknownTypeException;` );240 }241 }242 // try-catch block removed (caught by read)243 }244 private template addTag_elifIsType_add(T) {245 const addTag_elifIsType_add =246 `else if (tp == "`~T.stringof~`")`247 `addTag_add!(`~T.stringof~`) (id, dt);` ;248 }249 private void addTag_add(T) (ID id, char[] dt) {250 Arg!(T).Arg[id] = parse!(T) (dt);251 }252 253 void writeAll (ItemDelg itemdlg) {254 foreach (id, dt; _charA) itemdlg ("char[]", id, format!(char[])(dt));255 }256 debug void debugFunc () {}257 258 /* These make no attempt to check Arg is valid.259 * But if the symbol doesn't exist the complier will throw an error anyway, e.g.:260 * Error: identifier '_boolAA' is not defined261 */262 template Arg(T : T[]) {263 const ArgString = Arg!(T).ArgString ~ `A`;264 mixin(`alias `~ArgString~` Arg;`);265 }266 template Arg(T) {267 const ArgString = `_` ~ T.stringof;268 mixin(`alias `~ArgString~` Arg;`);269 }270 }271 272 /+class DynamicData : DataSection273 {274 void*[TypeInfo] data;275 276 }+/277 278 /+279 class TemplateData : DataSection280 {281 void addTag (char[] tp, ID id, char[] dt) {282 // runtime deduction of tp and aliasing?283 // CANNOT add data at runtime though.284 }285 // will this work? no idea.286 // templates can't be used to add non-static elements, so use a static array at index: this287 template Data(T,TemplateData* p) {288 static T[ID][TemplateData*] Data;289 }290 }291 +/292 293 96 unittest { // Only covers DataSet really. 294 97 DataSet ds = new DataSet; mde/mergetag/doc/file-format-text.txt
r4 r8 12 12 13 13 IDs: 14 IDs are used for several purposes; they are always stored as a uint number (0-4294967295). They may 15 be given in the file as a base-10 or hex number or, where a lookup table is provided to the reader, 16 as a double-quoted string (with no escape sequences). 14 IDs are used for several purposes; they are UTF-8 strings. They are stored in text files as unquoted strings; escape sequences are not supported and the strings should not contain the following characters, although this is not checked: <|=>{} 15 All characters between the appropriate markers are consumed into the ID, hence whitespace is meaningful. 17 16 Multiple section or data tags with the same ID are allowed; see the "Merging rules" section. 18 17 19 18 20 Outside of tags theonly whitespace or valid tags is allowed. Whitespace is ignored.19 Outside of tags only whitespace or valid tags is allowed. Whitespace is ignored. 21 20 The following tags are valid (see below for details): 22 21 tag purpose … … 29 28 30 29 Section identifier tags: 31 Format: {ID} or {ID|ID} 32 In the {ID|ID} case, the first ID is the section type, and the second ID the section name. 33 In the {ID} case, the section type ID has been ommitted and the default type is used (0). 34 A section identifier marks the beginning of a new section, extending until the next section 35 identifier or the end of the file. When a section is read, a new 30 Format: {ID} 31 The ID is the section identifier/name. The ID type is DefaultData unless overriden by the code using the reader. 32 A section identifier marks the beginning of a new section, extending until the next section identifier or the end of the file. 36 33 37 34 38 35 Data item tags: 39 36 Format: <tp|ID=dt> 40 A data item with type tp, identifier ID and data dt. If the data does not fit the given type it is 41 an error and the tag is ignored. Once split into a type string, ID and data string, the contents 42 are passed to an addTag() function within the DataSection class which will parse tags of a 43 recognised format and either ignore or print a warning about other tags. 37 A data item with type tp, identifier ID and data dt. If the data does not fit the given type it is an error and the tag is ignored. Once split into a type string, ID and data string, the contents are passed to an addTag() function within the DataSection class which will parse tags of a recognised format and either ignore or print a warning about other tags. 44 38 45 39 … … 54 48 t1[t2] an associative array with key-type t2 55 49 Possible future additions: 56 tp() a dynamic merging list of sub-type tp (only valid as the primary type, ie 57 <subtype()|...>, not a sub-type of a tuple or another dynamic list) 50 tp() a dynamic merging list of sub-type tp (only valid as the primary type, ie <subtype()|...>, not a sub-type of a tuple or another dynamic list) 58 51 {t1,t2,...,tn} a tuple with sub-types t1, t2, ..., tn 59 52 … … 108 101 y+zi, y-zi a complex number (4+0i may be written as 4, etc) (y, z are f.p.s) 109 102 0xz, -0xz a hexadecimal integer z (composed of chars 0-9,a-f,A-F) 110 'c' a char/wchar/dchar character, depending on the type specified (c may be any 111 single character except ' or an escape sequence) 112 "string" equivalent to ['s','t','r','i','n','g'] (for a string/wstring/dstring type) 113 may contain escape sequences 114 Escape sequences are a subset of those supported by D: \" \' \\ \a \b \f \n \r \t \v 103 'c' a char/wchar/dchar character, depending on the type specified (c may be any single character except ' or an escape sequence) 104 "string" equivalent to ['s','t','r','i','n','g'] --- may contain the following escape sequences as defined in D: \" \' \\ \a \b \f \n \r \t \v 115 105 XX...XX Binary (ubyte[]); each pair of chars is read as a hex ubyte 116 106 <void> void "data" has no symbols … … 124 114 Simple comment blocks: 125 115 Format: !{...} 126 This is a simple comment block, and only curly braces ({,}) are treated specially. A {, whether or 127 not it is preceded by a !, starts an embedded comment block, and a } ends either an embedded block 128 or the actual comment block. Note: beware commenting out {...} tags with a string ID containing 129 curly braces which aren't in matching pairs. 116 This is a simple comment block, and only curly braces ({,}) are treated specially. A {, whether or not it is preceded by a !, starts an embedded comment block, and a } ends either an embedded block or the actual comment block. Note: beware commenting out anything containing curly braces which aren't in matching pairs. 130 117 Commented data tags: 131 118 Format: !<tp|ID=dt> 132 Basically a commented out data tag. Conformance to the above spec may not be checked as strictly as 133 normal, but the dt section is checked for strings so that a > within a string won't end the tag. 119 Basically a commented out data tag. Conformance to the above spec may not be checked as strictly as normal, but the dt section is checked for strings so that a > within a string won't end the tag. 134 120 135 121 … … 150 136 151 137 Header: 152 The header is a standard section which is mandatory and must be the first section. Its section 153 identifier must start at the beginning of the file with no whitespace, declared with: 154 {MTXY} where XY is a two digit CAPITAL HEX version number representing the 155 mergetag format version, e.g. {MT01} . 138 The header is a standard section which is mandatory and must be the first section. Its section identifier must start at the beginning of the file with no whitespace, declared with: 139 {MTXY} where XY is a two digit CAPITAL HEX version number representing the mergetag format version, e.g. {MT01} . 156 140 If these are not the first 6 characters of the file the file will not be regarded as valid. 157 141 This formatting is very strict to allow reliable low-level parsing. … … 165 149 <*|"Version"=...> (use any supported type)&n
