Changeset 8:f63f4f41a2dc

Show
Ignore:
Timestamp:
01/25/08 13:17:38 (1 year ago)
Author:
Diggory Hardy <diggory.hardy@gmail.com>
branch:
default
convert_revision:
14f88df66181f95f3e457366d4a7ca2fba9d3afb
Message:

Big changes to init; got some way towards input event support; changed mergetag ID to char[] from uint.

committer: Diggory Hardy <diggory.hardy@gmail.com>

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • conf/input.mtt

    r7 r8  
    11{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  
    66Coding 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. 
    77 
    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.) 
     8Identifiers: 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 
     10Module/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 
     12Spelling: 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.) 
    913 
    1014 
    1115Package 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 
     18Module 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). 
    1219 
    1320 
     
    1825 
    1926In 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. 
    2330    Error   For errors which directly: 
    2431        • cut-short a (reasonably large) operation (e.g. reading a file). 
     
    2633    Fatal   For errors directly (i.e. definately and almost immediately) ending the program. 
    2734 
     35For 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. 
     36Thus: 
     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     
    2844 
    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 
     46Thrown 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  
    44#[mde/mergetag] 
    55#[mde/input] 
    6 [test/MTTest.d] 
    7 target=bin/MTTest 
     6#[test/MTTest.d] 
     7#target=bin/MTTest 
    88 
  • mde/events.d

    r4 r8  
    88import derelict.sdl.events; 
    99 
     10import tango.util.log.Log : Log, Logger; 
     11 
     12private Logger logger; 
     13static this() { 
     14    logger = Log.getLogger ("mde.events"); 
     15} 
     16 
    1017static bool run = true; 
    1118 
    12 static this () { 
     19void addEventsSchedule () { 
    1320    Scheduler.perFrame (&pollEvents); 
    1421} 
     
    1926        switch (event.type) { 
    2027            case SDL_QUIT: 
     28                logger.info ("Quit requested"); 
    2129                run = false; 
    22             break; 
     30                break; 
    2331            default: 
    24                 Input.instance() (event); 
     32                input (event); 
    2533        } 
    2634    } 
  • mde/exception.d

    r7 r8  
    2626    } 
    2727} 
    28  
    29 class DynamicLibraryLoadException : initException { 
    30     this (char[] msg) { 
    31         super("when loading dynamic library: " ~ msg); 
    32     } 
    33 } 
  • mde/init.d

    r6 r8  
    66module mde.init; 
    77 
    8 import mde.exception; 
     8public import mde.exception; 
    99 
     10import mde.events; 
    1011import mde.input.input; 
     12import mde.input.joystick; 
    1113 
    1214// tango imports 
     
    1416import tango.util.log.Log : Log, Logger; 
    1517import tango.util.log.ConsoleAppender : ConsoleAppender; 
     18import tango.stdc.stringz : fromUtf8z; 
    1619 
    1720import derelict.sdl.sdl; 
     
    4346scope class Init 
    4447{ 
    45     static Logger logger; 
     48    private static Logger logger; 
    4649    static this() { 
    4750        logger = Log.getLogger ("mde.init.Init"); 
     
    5255    * Runs general initialisation code, in a threaded manner where this isn't difficult. 
    5356    * 
    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. */ 
    5658    /* In a single-threaded function this could be done with: 
    5759    * scope(failure) cleanup; 
     
    6365    this() 
    6466    { 
     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         
    65110        // 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        } 
    68121         
    69122        // Do some initialisation in the main thread 
    70         Input.instance.loadConfig (0); 
     123        input.loadConfig ();        // (may also create instance) 
     124        addEventsSchedule (); 
    71125         
    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            } 
    75144        } 
    76         catch (initException e) {   // Any problems? 
     145         
     146        if (initFailure) { 
    77147            // All cleanup-on-failure must be done here. 
    78148            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."); 
    80152        } 
    81          
    82153    } 
    83154     
    84     /* Initialisation functions
     155    /** DTOR - runs cleanup
    85156    * 
    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. */ 
    102158    ~this() 
    103159    { 
    104         runCleanupFcts(); 
     160        runCleanupFcts();  // if threading, note not all functions can be called simultaeneously 
    105161    } 
    106162     
  • mde/input/config.d

    r7 r8  
    44debug import mde.text.format; 
    55 
    6 import mde.input.exception; 
     6public import mde.input.exception; 
    77 
    8 import mde.mergetag.read; 
     8import MT = mde.mergetag.read; 
    99import mde.text.parse; 
    1010 
     
    1212import tango.util.collection.TreeBag : TreeBag; 
    1313 
    14 Logger logger; 
     14private Logger logger; 
    1515static this() { 
    1616    logger = Log.getLogger ("mde.input.config"); 
     
    2222 * Class extends DataSection so that it can be loaded by mergetag easily. 
    2323 */ 
    24 class Config : DataSection 
     24class Config : MT.DataSection 
    2525{ 
    2626    alias uint[] outQueue;      // This is the type for the out queue config data. 
     
    8787    uint[] inheritants;     /// Other profiles to inherit. 
    8888     
    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(). 
    9190    private static TreeBag!(char[]) loadedFiles;    // all filenames load tried to read 
    9291     
     
    10099        if (loadedFiles.contains (filename)) return;    // forget it; already done that 
    101100        loadedFiles.add (filename); 
    102         Reader file; 
     101        MT.Reader file; 
    103102        try { 
    104             file = new Reader(filename, null, true);  // open and read header 
     103            file = new MT.Reader(filename, null, true);   // open and read header 
    105104            // TODO: also load user-config file 
    106105             
    107106            file.dataSecCreator = 
    108                function DataSection (ID) {    return new Config;  }; 
     107            function MT.DataSection (MT.ID) { return new Config;  }; 
    109108             
    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); 
    113113             
    114             if (configs_p)  file.read(cast(ID[]) *configs_p); // restrict to this set IF a restriction was given 
     114            if (configs_p)  file.read(*configs_p);    // restrict to this set IF a restriction was given 
    115115            else        file.read();        // otherwise read all 
    116116        } 
    117         catch (MTException) { 
     117        catch (MT.MTException) { 
    118118            logger.error ("Unable to load configs from: " ~ filename); 
    119119            throw new ConfigLoadException; 
    120120        } 
    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         
    124133        debug { 
    125134            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)); 
    127136            foreach (id, cfg; configs) { 
    128137                logger.trace ("Section "~format!(uint)(id)~": " ~ format!(uint[][uint])(cfg.button) ~ " (" ~ format!(uint)(cfg.dnbc) ~ ")"); 
     
    131140    } 
    132141     
    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    } 
    134146    private this() {}   // Private since this class should only be created from here. 
    135147     
    136     void addTag (char[] tp, ID id, char[] dt) { 
     148    void addTag (char[] tp, MT.ID id, char[] dt) { 
    137149        if (tp == "uint[][uint]") { 
    138150            if (id == QUEUE.BUTTON) { 
     
    143155            else if (id == QUEUE.AXIS) axis = cast(outQueue[uint]) parse!(uint[][uint]) (dt); 
    144156            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); 
    149158        } // FIXME: add support for name and inheritants. 
    150         else throw new MTUnknownTypeException ("Input Config: only uint[][uint] type supported"); 
     159        else throw new MT.MTUnknownTypeException ("Input Config: only uint[][uint] type supported"); 
    151160    } 
    152161    void writeAll (ItemDelg) { 
  • mde/input/exception.d

    r7 r8  
    11module mde.input.exception; 
    22 
    3 import mde.exception; 
     3public import mde.exception; 
    44 
    55/// Base Input exception class. 
  • mde/input/input.d

    r7 r8  
    77// package imports 
    88import mde.input.config; 
    9 import mde.input.exception; 
     9public import mde.input.exception; 
    1010 
    1111// sdl imports 
    1212import derelict.sdl.events; 
    1313 
     14import tango.util.log.Log : Log, Logger; 
     15 
     16Input input;            /// Global instance of input. 
     17private Logger logger; 
     18 
     19static this() { 
     20    input = new Input; 
     21     
     22    logger = Log.getLogger ("mde.input.config"); 
     23} 
     24 
     25/// Class encapsulating all input functionality. 
    1426class Input 
    1527{ 
     
    1931    alias void delegate(inputID, real)      AxisCallback; 
    2032    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     } 
    2933 
    3034    /** Get key status at this ID. 
     
    104108            case SDL_JOYBUTTONDOWN: 
    105109            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                } 
    106114                outQueue* p = (Config.B.JOYBUTTON | (event.jbutton.which << 12) | event.jbutton.button) in config.button; 
    107115                if (p) bEventOut (event.jbutton.state == 0x1, readOutQueue(*p)); 
    108             break; 
     116                break; 
    109117         
    110118            /+ 
     
    121129            +/ 
    122130            default: 
    123             return false; 
     131                return false; 
    124132        } 
    125133        return true; 
     
    128136    /** Resets relative movement of mice / joystick balls to zero. 
    129137    * 
    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). 
    131140    */ 
    132141    void frameReset () { 
     
    140149    * Returns: true if the requested config id wasn't found. 
    141150    */ 
    142     bool loadConfig (uint id) { 
     151    bool loadConfig () { 
    143152        Config.load("conf/input.mtt");  // FIXME: filename 
    144         Config* c_p = id in Config.configs; 
     153        Config* c_p = "Default" in Config.configs; 
    145154        if (c_p) { 
    146155            config = *c_p; 
    147156            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."); 
    150160        return true; 
    151161    } 
  • mde/mde.d

    r4 r8  
    1212 
    1313import mde.input.input; 
    14  
    15 import mde.mergetag.read; 
    1614 
    1715// External library imports 
     
    3735    } 
    3836     
    39     Input input = Input.instance(); 
    4037    input.addButtonCallback (cast(Input.inputID) 3u, delegate void(Input.inputID i, bool b) { 
    4138        Stdout ("Event: ")(i)(" changed to: ")(b).newline; 
     
    4340    bool oldb = false; 
    4441     
    45     /+while (run)+/ 
    46     for (ulong t = 0; t < 100; ++t)
     42    while (run) 
     43    /+for (ulong t = 0; t < 100; ++t)+/
    4744        Scheduler.run (Clock.now()); 
    4845         
  • 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
    22module mde.mergetag.dataset; 
    33 
    44// package imports 
    5 import mde.mergetag.exception; 
     5public import mde.mergetag.exception; 
     6public import mde.mergetag.defaultdata; // used in DataSet so it should be publically imported 
    67 
    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; 
    159 
    1610/** Typedef for data & section indexes (can be changed to ulong if necessary.) */ 
    17 typedef uint ID; 
     11typedef char[] ID; 
    1812 
    1913package struct MTFormatVersion { 
     
    3529} 
    3630 
    37 private Logger logger; 
     31/+private Logger logger; 
    3832static this () { 
    3933    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}+/ 
    5335 
    5436/************************************************************************************************** 
    5537 * 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. 
    5641 * 
    5742 * Any class implementing DataSection may be used to store data; by default a DefaultData class is 
     
    6348class DataSet 
    6449{ 
    65     DefaultData header;         /// Header 
     50    DefaultData header;         /// Header section. 
    6651    DataSection[ID] sec;        /// Dynamic array of sections 
    67      
    68     /// Return a reference to the indexed item 
    69     DataSection opIndex(ID i) { 
    70         return sec[i]; 
    71     } 
    7252     
    7353    /// Template to return all sections of a child-class type. 
     
    11494} 
    11595 
    116 /** 
    117  * Default DataSection class. 
    118  * 
    119  * Supports all the basic types currently supported and array versions of 
    120  * 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 not 
    126  * possible or maybe just because I don't know how to use templates properly) a lot of this code is 
    127  * 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 no 
    131  * need to store types and no need to check an argument's type for every access (this could be done 
    132  * by throwing errors, but then an incorrect (perhaps hand-edited) data file could cause a lot of 
    133  * errors to be thrown). 
    134  */ 
    135 class DefaultData : DataSection 
    136 { 
    137     //BEGIN DATA 
    138     /** Data Members 
    139      * 
    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 to 
    157      * --- alias Name Arg; --- 
    158      * actually being a mixin. 
    159      */ 
    160       
    161     bool    [ID]    _bool; 
    162     byte    [ID]    _byte;      /// ditto 
    163     short   [ID]    _short;     /// ditto 
    164     int     [ID]    _int;       /// ditto 
    165     long    [ID]    _long;      /// ditto 
    166     ubyte   [ID]    _ubyte;     /// ditto 
    167     ushort  [ID]    _ushort;    /// ditto 
    168     uint    [ID]    _uint;      /// ditto 
    169     ulong   [ID]    _ulong;     /// ditto 
    170      
    171     char    [ID]    _char;      /// ditto 
    172      
    173     float   [ID]    _float;     /// ditto 
    174     double  [ID]    _double;    /// ditto 
    175     real    [ID]    _real;      /// ditto 
    176      
    177     bool[]  [ID]    _boolA;     /// ditto 
    178     byte[]  [ID]    _byteA;     /// ditto 
    179     short[] [ID]    _shortA;    /// ditto 
    180     int[]   [ID]    _intA;      /// ditto 
    181     long[]  [ID]    _longA;     /// ditto 
    182     ubyte[] [ID]    _ubyteA;    /// ditto 
    183     ushort[]    [ID]    _ushortA;   /// ditto 
    184     uint[]  [ID]    _uintA;     /// ditto 
    185     ulong[] [ID]    _ulongA;    /// ditto 
    186      
    187     char[]  [ID]    _charA;     /// ditto 
    188      
    189     float[] [ID]    _floatA;    /// ditto 
    190     double[]    [ID]    _doubleA;   /// ditto 
    191     real[]  [ID]    _realA;     /// ditto 
    192      
    193     /** Alias names */ 
    194     alias   _ubyteA _binary; 
    195     alias   _charA  _string;    /// ditto 
    196     //END DATA 
    197      
    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 defined 
    261      */ 
    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 : DataSection 
    273 { 
    274     void*[TypeInfo] data; 
    275      
    276 }+/ 
    277  
    278 /+ 
    279 class TemplateData : DataSection 
    280 { 
    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: this 
    287     template Data(T,TemplateData* p) { 
    288         static T[ID][TemplateData*] Data; 
    289     } 
    290 } 
    291 +/ 
    292  
    29396unittest {  // Only covers DataSet really. 
    29497    DataSet ds = new DataSet; 
  • mde/mergetag/doc/file-format-text.txt

    r4 r8  
    1212 
    1313IDs: 
    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). 
     14IDs 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: <|=>{} 
     15All characters between the appropriate markers are consumed into the ID, hence whitespace is meaningful. 
    1716Multiple section or data tags with the same ID are allowed; see the "Merging rules" section. 
    1817 
    1918 
    20 Outside of tags the only whitespace or valid tags is allowed. Whitespace is ignored. 
     19Outside of tags only whitespace or valid tags is allowed. Whitespace is ignored. 
    2120The following tags are valid (see below for details): 
    2221tag     purpose 
     
    2928 
    3029Section 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  
     30Format: {ID} 
     31The ID is the section identifier/name. The ID type is DefaultData unless overriden by the code using the reader. 
     32A section identifier marks the beginning of a new section, extending until the next section identifier or the end of the file. 
    3633 
    3734 
    3835Data item tags: 
    3936Format: <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. 
     37A 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. 
    4438 
    4539 
     
    5448    t1[t2]      an associative array with key-type t2 
    5549Possible 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) 
    5851    {t1,t2,...,tn}  a tuple with sub-types t1, t2, ..., tn 
    5952 
     
    108101    y+zi, y-zi  a complex number (4+0i may be written as 4, etc) (y, z are f.p.s) 
    109102    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 
    115105    XX...XX     Binary (ubyte[]); each pair of chars is read as a hex ubyte 
    116106    <void>      void "data" has no symbols 
     
    124114Simple comment blocks: 
    125115Format: !{...} 
    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. 
     116This 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. 
    130117Commented data tags: 
    131118Format: !<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. 
     119Basically 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. 
    134120 
    135121 
     
    150136 
    151137Header: 
    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} . 
     138The 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} . 
    156140If these are not the first 6 characters of the file the file will not be regarded as valid. 
    157141This formatting is very strict to allow reliable low-level parsing. 
     
    165149    <*|"Version"=...>       (use any supported type)&n