Changeset 79:61ea26abe4dd

Show
Ignore:
Timestamp:
08/05/08 06:51:51 (5 months ago)
Author:
Diggory Hardy <diggory.hardy@gmail.com>
branch:
default
Message:

Moved mde/mergetag/parse/parse(To/From) to mde/mergetag/(de)serialize. Implemented (de)serialization of structs.

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • codeDoc/jobs.txt

    r75 r79  
    44 
    55In progress: 
    6 Redesigning how widgets are created and receive their data. 
    76 
    87 
     
    19183   Scheduler for drawing only windows which need redrawing. 
    20193   Update scheduler as outlined in FIXME. 
    21 3   Windows building/compatibility (currently partial) 
     203   Windows building/compatibility (currently partial) - tango/sys/win32/SpecialPath.d 
    22212   Remove ability to scan, then load, mergetag sections. Not so necessary with section creator callback and allows "sliding window" type partial buffering. 
    23222   Options need a "level": simple options, for advanced users, for debugging only, etc. 
  • mde/font/font.d

    r76 r79  
    3030import derelict.opengl.gl; 
    3131 
    32 import mde.mergetag.parse.parseTo : parseTo
     32import mde.mergetag.deserialize
    3333import tango.stdc.stringz; 
    3434import Util = tango.text.Util; 
     
    295295     
    296296    FT_Face face; 
     297     
     298    debug(mdeUnitTest) unittest { 
     299        // Don't do a unittest since font relies on loading the freetype library dynamically, 
     300        // normally done by Init. Also font is mostly visual and many problems will be obvious. 
     301    } 
    297302} 
    298303 
  • mde/gui/WidgetData.d

    r78 r79  
    3939import mde.mergetag.Writer; 
    4040import mde.setup.paths; 
    41 import mde.mergetag.parse.parseTo; 
    42 import mde.mergetag.parse.parseFrom : parseFrom; 
     41import mde.mergetag.serialize; 
    4342 
    4443import tango.core.sync.Mutex; 
  • mde/gui/content/options.d

    r73 r79  
    3838        foreach (i,s; list) { 
    3939            Translation.Entry transled = trans.getStruct (s); 
    40             textOpts[i] = new ContentOptionText(opts, s, transled.str, transled.desc); 
     40            textOpts[i] = new ContentOptionText(opts, s, transled.name, transled.desc); 
    4141        } 
    4242    } 
     
    4747     
    4848    static OptionList trial () { 
    49         return new OptionList (miscOpts, "OptionsMisc"); 
     49        return new OptionList (miscOpts, "L10n/OptionsMisc"); 
    5050    } 
    5151     
  • mde/input/Config.d

    r74 r79  
    2121import MT = mde.mergetag.Reader; 
    2222import mde.setup.paths; 
    23 import mde.mergetag.parse.parseTo : parseTo
    24 debug import mde.mergetag.parse.parseFrom : parseFrom
     23import mde.mergetag.deserialize
     24debug import mde.mergetag.serialize
    2525 
    2626import tango.util.log.Log : Log, Logger; 
  • mde/lookup/Options.d

    r75 r79  
    2929import mde.mergetag.DataSet; 
    3030import mde.mergetag.exception; 
    31 import mde.mergetag.parse.parseTo : parseTo; 
    32 import mde.mergetag.parse.parseFrom : parseFrom; 
     31import mde.mergetag.serialize; 
    3332 
    3433import tango.core.Exception : ArrayBoundsException; 
     
    203202     * must be changed in two separate places. */ 
    204203    void set(T) (char[] symbol, T val) { 
    205         static if (!TIsIn!(T,TYPES)) 
    206             static assert (false, "Options.set does not currently support type "~T.stringof); 
     204        static assert (TIsIn!(T,TYPES), "Options does not support type "~T.stringof); 
    207205         
    208206        mixin (`alias opts`~TName!(T)~` optsVars;`); 
     
    223221     * Using this method to read an option is not necessary, but allows for generic use.  */ 
    224222    T get(T) (char[] symbol) { 
    225         static if (!TIsIn!(T,TYPES)) 
    226             static assert (false, "Options.get does not currently support type "~T.stringof); 
     223        static assert (TIsIn!(T,TYPES), "Options does not support type "~T.stringof); 
    227224         
    228225        mixin (`alias opts`~TName!(T)~` optsVars;`); 
     
    238235    /** List the names of all options of a specific type. */ 
    239236    char[][] list(T) () { 
    240         static if (!TIsIn!(T,TYPES)) 
    241             static assert (false, "Options.list does not currently support type "~T.stringof); 
     237        static assert (TIsIn!(T,TYPES), "Options does not support type "~T.stringof); 
    242238         
    243239        mixin (`alias opts`~TName!(T)~` optsVars;`); 
     
    249245        OptionChanges optionChanges;    // all changes to options (for saving) 
    250246         
    251        // The "pointer lists": 
     247        // The "pointer lists", e.g. char[]*[ID] optscharA; 
    252248        mixin (PLists!(TYPES)); 
    253249    } 
     
    409405     
    410406    void set(T) (ID id, T x) { 
    411         static if (!TIsIn!(T,TYPES)) 
    412             static assert (false, "OptionChanges.set does not currently support type "~T.stringof); 
     407        static assert (TIsIn!(T,TYPES), "Options does not support type "~T.stringof); 
    413408         
    414409        mixin (`alias opts`~TName!(T)~` optsVars;`); 
  • mde/lookup/Translation.d

    r75 r79  
    4545import mde.mergetag.Reader; 
    4646import mde.mergetag.exception; 
    47 import mde.mergetag.parse.parseTo
     47import mde.mergetag.deserialize
    4848 
    4949import tango.util.log.Log : Log, Logger; 
     
    6969        Entry* p = id in entries; 
    7070        if (p) { 
    71             return p.str
     71            return p.name
    7272        } else { 
    7373            return id; 
     
    7979        if (p) { 
    8080            description = p.desc; 
    81             return p.str
     81            return p.name
    8282        } else { 
    8383            return id; 
     
    9292        } else { 
    9393            Entry ret; 
    94             ret.str = id; 
     94            ret.name = id; 
    9595            return ret; 
    9696        } 
     
    118118        IReader reader; 
    119119        try { 
    120             reader = dataDir.makeMTReader ("L10n/"~name, PRIORITY.HIGH_LOW); 
     120            reader = dataDir.makeMTReader (name, PRIORITY.HIGH_LOW); 
    121121            /* Note: we don't want to load every translation section depended on to its own class 
    122122            * instance, since we want to merge them. So make every mergetag section use the same 
     
    168168    void addTag (char[] tp, ID id, char[] dt) { 
    169169        if (tp == "entry") { 
    170             char[][] fields = split (stripBrackets (dt)); 
     170            // If the tag already exists, don't replace it 
     171            if (cast(char[]) id in entries) return; 
    171172             
    172             if (fields.length < 1) { 
    173                 // This tag is invalid, but this fact doesn't need to be reported elsewhere: 
     173            Entry entry = deserialize!(Entry) (dt); 
     174            if (entry.name is null) {   // This tag is invalid; ignore it 
    174175                logger.error ("For name "~name~", L10n "~L10n~": tag with ID "~cast(char[])id~" has no data"); 
    175176                return; 
    176177            } 
    177             // If the tag already exists, don't replace it 
    178             if (cast(char[]) id in entries) return; 
    179              
    180             Entry entry; 
    181             entry.str = parseTo!(char[]) (fields[0]); 
    182              
    183             if (fields.length >= 2) 
    184                 entry.desc = parseTo!(char[]) (fields[1]); 
    185              
    186178            entries[cast(char[]) id] = entry; 
    187179        } else if (tp == "char[][]") { 
     
    198190     */ 
    199191    struct Entry { 
    200         char[] str;         // The translated string 
     192        char[] name;        // The translated string 
    201193        char[] desc;        // An optional description 
    202194    } 
     
    237229        miscOpts.L10n = "test-1"; 
    238230         
    239         Translation transl = load ("i18nUnitTest"); 
     231        Translation transl = load ("unittest/Translation"); 
    240232         
    241233        // Simple get-string, check dependancy's entry doesn't override 
  • mde/mergetag/DataSet.d

    r26 r79  
    6464        ds.sec[cast(ID)"test"] = new DefaultData; 
    6565        assert (ds.getSections!(DefaultData)().length == 1); 
    66         ds.sec[cast(ID)"test"].addTag ("int",cast(ID)"T"," -543 "); 
    67         assert (ds.getSections!(DefaultData)()[cast(ID)"test"]._int[cast(ID)"T"] == -543); 
     66        ds.sec[cast(ID)"test"].addTag ("char[]",cast(ID)"T"," \"ut tag 1 \" "); 
     67        assert (ds.getSections!(DefaultData)()[cast(ID)"test"].Arg!(char[])[cast(ID)"T"] == "ut tag 1 "); 
    6868     
    6969        logger.info ("Unittest complete."); 
  • mde/mergetag/DefaultData.d

    r75 r79  
    2222import mde.mergetag.exception; 
    2323 
    24 import mde.mergetag.parse.parseTo : parseTo; 
    25 import mde.mergetag.parse.parseFrom : parseFrom; 
     24import mde.mergetag.serialize; 
    2625 
    2726 
     
    2928 * Default DataSection class. 
    3029 *  
    31  * Currently this is only used for headers, and thus the list of supported types has been 
     30 * Supported types are given by dataTypes. 
     31 *  
     32 * Currently DefaultData is only used for headers, and thus the list of supported types has been 
    3233 * reduced to just those used in headers. Load order is HIGH_LOW, i.e. existing entries aren't 
    3334 * overwritten. 
    34  *  
    35  * It did supports most of the basic types supported by D (excluding cent/ucent and 
    36  * imaginary/complex types) and array versions of each of these types, plus arrays of strings. 
    37  * 
    38  * Extending the class to support more types, even custom types, shouldn't be particularly 
    39  * difficult provided mde.text.parseTo and mde.text.parseFrom are extended to support the new 
    40  * types. 
    4135 *************************************************************************************************/ 
    4236/* The implementation now uses a fair bit of generic programming. Adjusting the types supported 
  • mde/mergetag/deserialize.d

    r70 r79  
    11/************************************************************************************************** 
     2 * Generic deserialization templated function. 
     3 * 
    24 * copyright: Copyright (c) 2007-2008 Diggory Hardy. 
    35 * 
    46 * author: Diggory Hardy, diggory.hardy@gmail.com 
    57 * 
    6  * license: BSD style: $(LICENSE) 
    7  * 
    8  * This contains templates for converting a char[] to various data-types. 
    9  * 
    10  * parseTo is roughly the inverse of $(B parseFrom) and should read any data output by $(B parseFrom). 
    11  * It is also available in tango.scrapple. 
    12  * 
    13  * This module basically implements the following templated function for most basic D types: 
    14  * bool, byte, short, int, long, ubyte, ushort, uint, ulong, float, double, real, char. 
    15  * It also supports arrays and associative arrays of any supported type (including of other arrays) 
    16  * and has special handling for strings (char[]) and binary (ubyte[]) data-types. 
    17  * ----------------------------- 
    18  * T parseTo(T) (char[] source); 
    19  * ----------------------------- 
    20  * 
    21  * $(I source) is the string to parse, and data of the templated type that is read from the string 
    22  * is returned. See the examples to get a better idea of its use. 
    23  * 
    24  * Syntax: 
    25  * The syntax for parsing $(I source) is mostly the same used by D without any prefixes/suffixes 
    26  * (except 0x, 0b & 0o base specifiers). Also a special ubyte[] syntax is supported; see examples. 
    27  * The following escape sequences are supported for strings and characters: \' \" \\ 
    28  * \a \b \f \n \r \t \v . Associative array literals use the same syntax as D, described here: 
    29  * $(LINK http://www.digitalmars.com/d/2.0/expression.html#AssocArrayLiteral). All whitespace is 
    30  * ignored (except of course within strings). 
     8 * Supports: 
     9 *  Associative arrays, arrays (inc. strings), structs, char types, bool, int types, float types. 
    3110 * 
    3211 * There are also some public utility functions with their own documentation. 
     
    3615 * suitable message. No other exceptions should be thrown. 
    3716 * 
    38  * Remarks: 
    39  * There is currently no support for reading wchar/dchar strings. There are, however, unicode 
    40  * conversions for converting UTF-8 to UTF-16/32. Be careful if converting on a char-by-char basis; 
    41  * such conversions cannot be used for non-ascii characters. 
    42  * 
    4317 * Examples: 
    4418 * ------------------------------------------------------------------------------------------------ 
    4519 * // Basic examples: 
    46  * ulong        a = parseTo!(ulong) ("20350"); 
    47  * float        d = parseTo!(float) ("  1.2e-9 "); 
    48  * int[]        b = parseTo!(int[]) ("[0,1,2,3]"); 
     20 * ulong        a = deserialize!(ulong) ("20350"); 
     21 * float        d = deserialize!(float) ("  1.2e-9 "); 
     22 * int[]        b = deserialize!(int[]) ("[0,1,2,3]"); 
    4923 * 
    5024 * // String and char[] syntax: 
    51  * char[]       c = parseTo!(char[]) ("\"A string\""); 
    52  * char[]       e = parseTo!(char[]) ("['a','n','o','t','h','e','r', ' ' ,'s','t','r','i','n','g']"); 
     25 * char[]       c = deserialize!(char[]) ("\"A string\""); 
     26 * char[]       e = deserialize!(char[]) ("['a','n','o','t','h','e','r', ' ' ,'s','t','r','i','n','g']"); 
    5327 * 
    5428 * // These be used interchangably; here's a more complex example of an associative array: 
    55  * bool[char[]] f = parseTo!(bool[char[]]) ("[ \"one\":true, ['t','w','o']:false, \"three\":1, \"four\":000 ]"); 
     29 * bool[char[]] f = deserialize!(bool[char[]]) ("[ \"one\":true, ['t','w','o']:false, \"three\":1, \"four\":000 ]"); 
    5630 * 
    5731 * // There is also a special notation for ubyte[] types: 
    5832 * // The digits following 0x must be in pairs and each specify one ubyte. 
    59  * assert ( parseTo!(ubyte[]) (`0x01F2AC`) == parseTo!(ubyte[]) (`[01 ,0xF2, 0xAC]`) ); 
     33 * assert ( deserialize!(ubyte[]) (`0x01F2AC`) == deserialize!(ubyte[]) (`[01 ,0xF2, 0xAC]`) ); 
    6034 * 
    6135 * // There's no limit to the complexity! 
    6236 * char[char[][][][char]][bool] z = ...; // don't expect me to write this! 
    6337 * ------------------------------------------------------------------------------------------------ 
     38 * 
     39 * TODO: Optimize memory allocation (if possible?). Test best sizes for initial allocations 
     40 * instead of merely guessing? 
    6441 *************************************************************************************************/ 
    65  
    66 module mde.mergetag.parse.parseTo; 
     42//NOTE: in case of multiple formats, make this a dummy module importing both serialize modules, 
     43// or put all the code here. 
     44module mde.mergetag.deserialize; 
    6745 
    6846// tango imports 
     
    7452 
    7553/** 
    76  * Base class for parseTo exceptions. 
     54 * Base class for deserialize exceptions. 
    7755 */ 
    7856class ParseException : TextException 
     
    8462} 
    8563 
    86  
    87 //BEGIN parseTo templates 
     64alias deserialize parseTo;      // support the old name 
     65 
     66//BEGIN deserialize templates 
    8867 
    8968// Associative arrays 
    9069 
    91 const char[] AA_ERR = "Invalid associative array: "; 
    92 T[S] parseTo(T : T[S], S) (char[] src) { 
     70T[S] deserialize(T : T[S], S) (char[] src) { 
    9371    src = Util.trim(src); 
    9472    if (src.length < 2 || src[0] != '[' || src[$-1] != ']') 
    95         throw new ParseException (AA_ERR ~ "not [ ... ]"); // bad braces. 
     73        throw new ParseException ("Invalid associative array: not [ ... ]");  // bad braces. 
    9674     
    9775    T[S] ret; 
    9876    foreach (char[] pair; split (src[1..$-1])) { 
    9977        uint i = 0; 
    100         while (i < pair.length) {  // advance to the ':' 
     78        while (i < pair.length) {   // advance to the ':' 
    10179            char c = pair[i]; 
    10280            if (c == ':') break; 
    103             if (c == '\'' || c == '"') {   // string or character 
     81            if (c == '\'' || c == '"') {    // string or character 
    10482                ++i; 
    10583                while (i < pair.length && pair[i] != c) { 
    106                     if (pair[i] == '\\') { 
    107                         if (i+2 >= pair.length) throw new ParseException (AA_ERR ~ "unfinished escape sequence within string/char"); 
    108                         ++i;    // escape seq. 
    109                     } 
     84                    if (pair[i] == '\\') 
     85                        ++i;    // escape seq. 
    11086                    ++i; 
    11187                } 
    112                 if (i == pair.length) { 
    113                     throw new ParseException (AA_ERR ~ "encountered [ ... KEY] (missing :DATA)"); 
    114                 } 
     88                // Could have an unterminated ' or " causing i >= pair.length, but: 
     89                // 1. Impossible: split would have thrown 
     90                // 2. In any case this would be caught below. 
    11591            } 
    11692            ++i; 
    11793        } 
    118         if (i == pair.length) { 
    119             throw new ParseException (AA_ERR ~ "encountered [ ... KEY:] (missing DATA)"); 
    120         } 
    121         ret[parseTo!(S) (pair[0..i])] = parseTo!(T) (pair[i+1..$]); 
     94        if (i >= pair.length) 
     95            throw new ParseException ("Invalid associative array: encountered [ ... KEY] (missing :DATA)"); 
     96        ret[deserialize!(S) (pair[0..i])] = deserialize!(T) (pair[i+1..$]); 
    12297    } 
    12398    return ret; 
    12499} 
    125 debug (UnitTest) unittest { 
    126     char[][char] X = parseTo!(char[][char]) (`['a':"animal", 'b':['b','u','s']]`); 
    127     char[][char] Y = ['a':cast(char[])"animal", 'b':['b','u','s']]; 
    128      
    129     //FIXME: when the compiler's fixed: http://d.puremagic.com/issues/show_bug.cgi?id=1671 
    130     // just assert (X == Y) 
    131     assert (X.length == Y.length); 
    132     assert (X.keys == Y.keys); 
    133     assert (X.values == Y.values); 
    134     //X.rehash; Y.rehash;   // doesn't make a difference 
    135     //assert (X == Y);      // fails (compiler bug) 
    136 } 
    137100 
    138101 
    139102// Arrays 
    140103 
    141 T[] parseTo(T : T[]) (char[] src) { 
     104T[] deserialize(T : T[]) (char[] src) { 
    142105    src = Util.trim(src); 
    143     if (src.length >= 2 && src[0] == '[' && src[$-1] == ']') return toArray!(T[]) (src); 
    144     throw new ParseException ("Invalid array: not [x, ..., z]"); 
     106    if (src.length >= 2 && src[0] == '[' && src[$-1] == ']') 
     107        return toArray!(T[]) (src); 
     108    throw new ParseException ("Invalid array: not [ ... ]"); 
    145109} 
    146110 
    147111// String (array special case) 
    148 T parseTo(T : char[]) (char[] src) { 
     112T deserialize(T : char[]) (char[] src) { 
    149113    src = Util.trim(src); 
    150114    if (src.length >= 2 && src[0] == '"' && src[$-1] == '"') { 
    151115        src = src[1..$-1]; 
    152116        T ret; 
    153         ret.length = src.length;   // maximum length; retract to actual length later 
     117        ret.length = src.length;    // maximum length; retract to actual length later 
    154118        uint i = 0; 
    155119        for (uint t = 0; t < src.length;) { 
    156120            // process a block of non-escaped characters 
    157121            uint s = t; 
    158             while (t < src.length && src[t] != '\\') ++t;  // non-escaped characters 
     122            while (t < src.length && src[t] != '\\') ++t;   // non-escaped characters 
    159123            uint j = i + t - s; 
    160             ret[i..j] = src[s..t]; // copy a block 
     124            ret[i..j] = src[s..t];  // copy a block 
    161125            i = j; 
    162126             
     
    164128            while (t < src.length && src[t] == '\\') { 
    165129                t++; 
    166                 if (t == src.length) throw new ParseException ("Invalid string: ends \\\" !");  // next char is " 
    167                 ret[i++] = replaceEscapedChar (src[t++]);   // throws if it's invalid 
     130                if (t == src.length) 
     131                    throw new ParseException ("Invalid string: ends \\\" !");  // next char is " 
     132                ret[i++] = unEscapeChar (src[t++]);   // throws if it's invalid 
    168133            } 
    169134        } 
    170135        return ret[0..i]; 
    171136    } 
    172     else if (src.length >= 2 && src[0] == '[' && src[$-1] == ']') return toArray!(T) (src); 
     137    else if (src.length >= 2 && src[0] == '[' && src[$-1] == ']') 
     138        return toArray!(T) (src); 
    173139    throw new ParseException ("Invalid string: not quoted (\"*\") or char array (['a',...,'c'])"); 
    174140} 
    175141// Unicode conversions for strings: 
    176 T parseTo(T : wchar[]) (char[] src) { 
     142T deserialize(T : wchar[]) (char[] src) { 
    177143    // May throw a UnicodeException; don't bother catching and rethrowing: 
    178     return Utf.toString16 (parseTo!(char[]) (src)); 
    179 } 
    180 T parseTo(T : dchar[]) (char[] src) { 
     144    return Utf.toString16 (deserialize!(char[]) (src)); 
     145} 
     146T deserialize(T : dchar[]) (char[] src) { 
    181147    // May throw a UnicodeException; don't bother catching and rethrowing: 
    182     return Utf.toString32 (parseTo!(char[]) (src)); 
     148    return Utf.toString32 (deserialize!(char[]) (src)); 
    183149} 
    184150 
    185151// Binary (array special case) 
    186 T parseTo(T : ubyte[]) (char[] src) { 
     152T deserialize(T : ubyte[]) (char[] src) { 
    187153    src = Util.trim(src); 
    188154    // Standard case: 
     
    193159         
    194160        // Must be in pairs: 
    195         if (src.length % 2 == 1) throw new ParseException ("Invalid binary: odd number of chars"); 
     161        if (src.length % 2 == 1) 
     162            throw new ParseException ("Invalid binary: odd number of chars"); 
    196163         
    197164        T ret; 
    198         ret.length = src.length / 2;   // exact 
     165        ret.length = src.length / 2;    // exact 
    199166         
    200167        for (uint i, pos; pos + 1 < src.length; ++i) { 
     
    208175} 
    209176 
    210 debug (UnitTest) unittest { 
    211     assert (parseTo!(double[]) (`[1.0,1.0e-10]`) == [1.0, 1.0e-10]);   // generic array stuff 
    212     assert (parseTo!(double[]) (`[     ]`) == cast(double[]) []);      // empty array 
    213      
    214     // char[] and char conversions, with commas, escape sequences and multichar UTF8 characters: 
    215     assert (parseTo!(char[][]) (`[ ".\"", [',','\''] ,"!\b€" ]`) == [ ".\"".dup, [',','\''] ,"!\b€" ]); 
    216      
    217     // wchar[] and dchar[] conversions: 
    218     // The characters were pretty-much pulled at random from unicode tables. 
    219     // The last few cause some wierd (display only) effects in my editor. 
    220     assert (parseTo!(wchar[]) ("\"Test string: Â¶Î±ØŸà€ 
    221 àžáˆ€æ€\"") == "Test string: Â¶Î±ØŸà€ 
    222 àžáˆ€æ€"w); 
    223     assert (parseTo!(dchar[]) ("\"Test string: Â¶Î±ØŸà€ 
    224 àžáˆ€æ€\"") == "Test string: Â¶Î±ØŸà€ 
    225 àžáˆ€æ€"d); 
    226      
    227     assert (parseTo!(ubyte[]) (`0x01F2AC`) == cast(ubyte[]) [0x01, 0xF2, 0xAC]);    // ubyte[] special notation 
    228     assert (parseTo!(ubyte[]) (`[01 ,0xF2, 0xAC]`) == cast(ubyte[]) [0x01, 0xF2, 0xAC]);    // ubyte[] std notation 
    229 } 
    230  
    231177 
    232178// Basic types 
    233179 
    234180// Char 
    235 T parseTo(T : char) (char[] src) { 
     181// Assumes value is <= 127 (for valid UTF-8), since input would be invalid UTF-8 if not anyway. 
     182// (And we're not really interested in checking for valid unicode; char[] conversions don't either.) 
     183T deserialize(T : char) (char[] src) { 
    236184    src = Util.trim(src); 
    237185    if (src.length < 3 || src[0] != '\'' || src[$-1] != '\'') 
    238         throw new ParseException ("Invalid char: not quoted (e.g. 'c')"); 
    239     if (src[1] != '\\' && src.length == 3) return src[1];   // Either non escaped 
    240     if (src.length == 4) return replaceEscapedChar (src[2]);    // Or escaped 
    241      
    242     // Report various errors; warnings for likely and difficult to tell cases: 
    243     // Warn in case it's a multibyte UTF-8 character: 
    244     if (src[1] & 0xC0u) throw new UnicodeException ("Invalid char: too long (non-ASCII UTF-8 characters cannot be read as a single character)", 1); 
    245     throw new ParseException ("Invalid char: too long"); 
    246 
    247 /* Basic unicode convertions for wide-chars. 
    248 * NOTE: c > 127 signals the start of a multibyte UTF-8 sequence which must be converted for 
    249 * UTF-16/32. But since we don't know what the next bytes are we can't do the conversion. */ 
    250 const char[] WIDE_CHAR_ERROR = "Error: unicode non-ascii character cannot be converted from a single UTF-8 char"; 
    251 T parseTo(T : wchar) (char[] src) { 
    252     char c = parseTo!(char) (src); 
    253     if (c <= 127u) return cast(wchar) c;    // this char can be converted 
    254     else throw new UnicodeException (WIDE_CHAR_ERROR, 1); 
    255 
    256 T parseTo(T : dchar) (char[] src) { 
    257     char c = parseTo!(char) (src); 
    258     if (c <= 127u) return cast(dchar) c;    // this char can be converted 
    259     else throw new UnicodeException (WIDE_CHAR_ERROR, 1); 
     186        throw new ParseException ("Invalid char: not 'x' or '\\x'"); 
     187    if (src[1] != '\\') { 
     188        if (src.length == 3) 
     189            return src[1];              // Either non escaped 
     190        throw new ParseException ("Invalid char: too long (or non-ASCII)"); 
     191    } else if (src.length == 4) 
     192        return unEscapeChar (src[2]);   // Or escaped 
     193     
     194    throw new ParseException ("Invalid char: '\\'"); 
     195
     196// Basic unicode convertions for wide-chars. 
     197// Assumes value is <= 127 as does deserialize!(char). 
     198T deserialize(T : wchar) (char[] src) { 
     199    return cast(T) deserialize!(char) (src); 
     200
     201T deserialize(T : dchar) (char[] src) { 
     202    return cast(T) deserialize!(char) (src); 
     203
     204 
     205// Bool 
     206T deserialize(T : bool) (char[] src) { 
     207    src = Util.trim(src); 
     208    if (src == "true") 
     209        return true; 
     210    if (src == "false") 
     211        return false; 
     212    uint pos; 
     213    while (src.length > pos && src[pos] == '0') ++pos;  // skip leading zeros 
     214    if (src.length == pos && pos > 0) 
     215        return false; 
     216    if (src.length == pos + 1 && src[pos] == '1') 
     217        return true; 
     218    throw new ParseException ("Invalid bool: not true or false and doesn't evaluate to 0 or 1"); 
     219
     220 
     221// Ints 
     222T deserialize(T : byte) (char[] src) { 
     223    return toTInt!(T) (src); 
     224
     225T deserialize(T : short) (char[] src) { 
     226    return toTInt!(T) (src); 
     227
     228T deserialize(T : int) (char[] src) { 
     229    return toTInt!(T) (src); 
     230
     231T deserialize(T : long) (char[] src) { 
     232    return toTInt!(T) (src); 
     233
     234T deserialize(T : ubyte) (char[] src) { 
     235    return toTInt!(T) (src); 
     236
     237T deserialize(T : ushort) (char[] src) { 
     238    return toTInt!(T) (src); 
     239
     240T deserialize(T : uint) (char[] src) { 
     241    return toTInt!(T) (src); 
     242
     243T deserialize(T : ulong) (char[] src) { 
     244    return toTInt!(T) (src); 
    260245} 
    261246debug (UnitTest) unittest { 
    262     assert (parseTo!(char) ("\'\\\'\'") == '\''); 
    263     assert (parseTo!(wchar) ("'X'") == 'X'); 
    264     assert (parseTo!(dchar) ("'X'") == 'X'); 
    265 
    266  
    267 // Bool 
    268 T parseTo(T : bool) (char[] src) { 
     247    assert (deserialize!(byte) ("-5") == cast(byte) -5); 
     248    // annoyingly, octal syntax differs from D (blame tango): 
     249    assert (deserialize!(uint[]) ("[0b0100,0o724,0xFa59c,0xFFFFFFFF,0]") == [0b0100u,0724,0xFa59c,0xFFFFFFFF,0]); 
     250
     251 
     252// Floats 
     253T deserialize(T : float) (char[] src) { 
     254    return toTFloat!(T) (src); 
     255
     256T deserialize(T : double) (char[] src) { 
     257    return toTFloat!(T) (src); 
     258
     259T deserialize(T : real) (char[] src) { 
     260    return toTFloat!(T) (src); 
     261
     262 
     263 
     264// Structs 
     265T deserialize(T) (char[] src) { 
     266    static assert (is(T == struct), "Unsupported type: "~typeof(T)); 
     267     
    269268    src = Util.trim(src); 
    270     if (src == "true") return true; 
    271     if (src == "false") return false; 
    272     uint pos; 
    273     while (src.length > pos && src[pos] == '0') ++pos;  // skip leading zeros 
    274     if (src.length == pos && pos > 0) return false; 
    275     if (src.length == pos + 1 && src[pos] == '1') return true; 
    276     throw new ParseException ("Invalid bool: not true or false and doesn't evaluate to 0 or 1"); 
    277 
    278 debug (UnitTest) unittest { 
    279     assert (parseTo!(bool[]) (`[true,false,01,00]`) == cast(bool[]) [1,0,1,0]); 
    280 
    281  
    282 // Ints 
    283 T parseTo(T : byte) (char[] src) { 
    284     return toTInt!(T) (src); 
    285 
    286 T parseTo(T : short) (char[] src) { 
    287     return toTInt!(T) (src); 
    288 
    289 T parseTo(T : int) (char[] src) { 
    290     return toTInt!(T) (src); 
    291 
    292 T parseTo(T : long) (char[] src) { 
    293     return toTInt!(T) (src); 
    294 
    295 T parseTo(T : ubyte) (char[] src) { 
    296     return toTInt!(T) (src); 
    297 
    298 T parseTo(T : ushort) (char[] src) { 
    299     return toTInt!(T) (src); 
    300 
    301 T parseTo(T : uint) (char[] src) { 
    302     return toTInt!(T) (src); 
    303 
    304 T parseTo(T : ulong) (char[] src) { 
    305     return toTInt!(T) (src); 
    306 
    307 debug (UnitTest) unittest { 
    308     assert (parseTo!(byte) ("-5") == cast(byte) -5); 
    309     // annoyingly, octal syntax differs from D (blame tango): 
    310     assert (parseTo!(uint[]) ("[0b0100,0o724,0xFa59c,0xFFFFFFFF,0]") == [0b0100u,0724,0xFa59c,0xFFFFFFFF,0]); 
    311 
    312  
    313 // Floats 
    314 T parseTo(T : float) (char[] src) { 
    315     return toTFloat!(T) (src); 
    316 
    317 T parseTo(T : double) (char[] src) { 
    318     return toTFloat!(T) (src); 
    319 
    320 T parseTo(T : real) (char[] src) { 
    321     return toTFloat!(T) (src); 
    322 
    323 debug (UnitTest) unittest { 
    324     assert (parseTo!(float) ("0.0") == 0.0f); 
    325     assert (parseTo!(double) ("-1e25") == -1e25); 
    326     assert (parseTo!(real) ("5.24e-269") == cast(real) 5.24e-269); 
    327 
    328 //END parseTo templates 
     269    if (src.length < 2 || src[0] != '{' || src[$-1] != '}') 
     270        throw new ParseException ("Invalid struct: not { ... }"); 
     271     
     272    // cannot access elements of T.tupleof with non-const key, so use a type which can be 
     273    // accessed with a non-const key to store slices: 
     274    char[][T.tupleof.length] temp; 
     275    foreach (char[] pair; split (src[1..$-1])) { 
     276        uint i = 0; 
     277        while (i < pair.length) {   // advance to the ':' 
     278            char c = pair[i]; 
     279            if (c == ':') 
     280                break; 
     281            // key must be an int so no need for string checks 
     282            ++i; 
     283        } 
     284        if (i >= pair.length) 
     285            throw new ParseException ("Invalid struct: encountered { ... KEY} (missing :DATA)"); 
     286         
     287        size_t k = deserialize!(size_t) (pair[0..i]); 
     288        // Note: could check no entry was already stored in temp. 
     289        temp[k] = pair[i+1..$]; 
     290    } 
     291    T ret; 
     292    setStruct (ret, temp); 
     293    return ret; 
     294
     295//END deserialize templates 
    329296 
    330297//BEGIN Utility funcs 
    331 /** Trims whitespace at ends of string and checks for and removes array brackets: [] 
    332 * 
    333 * Throws: 
    334 *   ParseException if brackets aren't end non-whitespace characters. 
    335 * 
    336 * Returns: 
    337 *   String without brackets (and whitespace outside those brackets). Useful for passing to split. 
    338 */ 
    339 char[] stripBrackets (char[] src) { 
    340     src = Util.trim(src); 
    341     if (src.length >= 2 && src[0] == '[' && src[$-1] == ']') return src[1..$-1]; 
    342     throw new ParseException ("Invalid bracketed string: not [...]"); 
    343 } 
    344  
    345298/** Splits a string into substrings separated by '$(B ,)' with support for characters and strings 
    346299 * containing escape sequences and for embedded arrays ($(B [...])). 
    347300 * 
    348301 * Params: 
    349  *     src A string to separate on commas. Where used for parsing arrays, the brackets enclosing 
    350  *     the array should be removed before calling this function (stripBrackets can do this). 
     302 *     src A string to separate on commas. It shouldn't have enclosing brackets. 
    351303 * 
    352304 * Returns: 
     
    359311 *     fact no brackets are stripped from src. 
    360312 */ 
     313//FIXME foreach struct is more efficient 
    361314char[][] split (char[] src) { 
    362315    src = Util.trim (src); 
    363     if (src == "") return [];       // empty array: no elements when no data 
    364      
    365     uint depth = 0;         // surface depth (embedded arrays) 
     316    if (src == "") 
     317        return [];       // empty array: no elements when no data 
     318     
     319    uint depth = 0;         // surface depth (embedded arrays) 
    366320    char[][] ret; 
    367     ret.length = src.length / 3;   // unlikely to need a longer array 
    368     uint k = 0;                // current split piece 
    369     uint i = 0, j = 0;         // current read location, start of current piece 
     321    ret.length = src.length / 3;    // unlikely to need a longer array 
     322    uint k = 0;             // current split piece 
     323    uint i = 0, j = 0;          // current read location, start of current piece 
    370324     
    371325    while (i < src.length) { 
    372326        char c = src[i]; 
    373         if (c == '\'' || c == '"') {   // string or character 
     327        if (c == '\'' || c == '"') {    // string or character 
    374328            ++i; 
    375329            while (i < src.length && src[i] != c) { 
    376                 if (src[i] == '\\') ++i;    // escape seq. 
     330                if (src[i] == '\\') 
     331                    ++i;    // escape seq. 
    377332                ++i; 
    378             }  // Doesn't throw if no terminal quote at end of src, but this should be caught later. 
     333            }   // Doesn't throw if no terminal quote at end of src, but this should be caught later. 
    379334        } 
    380335        else if (c == '[') ++depth; 
    381336        else if (c == ']') { 
    382             if (depth) --depth; 
     337            if (depth) 
     338                --depth; 
    383339            else throw new ParseException ("Invalid array literal: closes before end of data item."); 
    384340        } 
    385         else if (c == ',' && depth == 0) {      // only if not an embedded array 
    386             if (ret.length <= k) ret.length = ret.length * 2; 
    387             ret[k++] = src[j..i];   // add this piece and increment k 
     341        else if (c == ',' && depth == 0) {      // only if not an embedded array 
     342            if (ret.length <= k) 
     343                ret.length = ret.length * 2; 
     344            ret[k++] = src[j..i];   // add this piece and increment k 
    388345            j = i + 1; 
    389346        } 
    390347        ++i; 
    391348    } 
    392     if (ret.length <= k) ret.length = k + 1; 
    393     ret[k] = src[j..i];     // add final piece (i >= j) 
     349    if (i > src.length) 
     350        throw new ParseException ("Unterminated quote (\' or \")"); 
     351     
     352    if (ret.length <= k) 
     353        ret.length = k + 1; 
     354    ret[k] = src[j..i];     // add final piece (i >= j) 
    394355    return ret[0..k+1]; 
    395356} 
     
    410371     
    411372    ate = cInt.trim (src, sign, radix); 
    412     if (ate == src.length) throw new ParseException ("Invalid integer: no digits"); 
     373    if (ate == src.length) 
     374        throw new ParseException ("Invalid integer: no digits"); 
    413375    ulong val = cInt.convert (src[ate..$], radix, &ate2); 
    414376    ate += ate2; 
     
    417379        throw new ParseException ("Invalid integer at marked character: \"" ~ src[0..ate] ~ "'" ~ src[ate] ~ "'" ~ src[ate+1..$] ~ "\""); 
    418380     
    419     if (val > TInt.max) throw new ParseException (INT_OUT_OF_RANGE); 
     381    if (val > TInt.max) 
     382        throw new ParseException (INT_OUT_OF_RANGE); 
    420383    if (sign) { 
    421384        long sval = cast(long) -val; 
    422         if (sval > TInt.min) return cast(TInt) sval; 
     385        if (sval > TInt.min) 
     386            return cast(TInt) sval; 
    423387        else throw new ParseException (INT_OUT_OF_RANGE); 
    424388    } 
     
    431395    // NOTE: As for toTInt(), this needs to strip leading as well as trailing whitespace. 
    432396    src = Util.trim (src); 
    433     if (src == "") throw new ParseException ("Invalid float: no digits"); 
     397    if (src == "") 
     398        throw new ParseException ("Invalid float: no digits"); 
    434399    uint ate; 
    435400     
     
    441406 * subset of those supported by D: \" \' \\ \a \b \f \n \r \t \v 
    442407 */ 
    443 private char replaceEscapedChar (char c) 
     408private char unEscapeChar (char c) 
    444409{ 
    445410    // This code was generated: 
     
    479444     
    480445    // if we haven't returned: 
    481     throw new ParseException ("Invalid escape sequence: \\"~c); 
     446    throw new ParseException ("Bad escape sequence: \\"~c); 
    482447} 
    483448 
     
    496461// Assumes input is of form "[xxxxx]" (i.e. first and last chars are '[', ']' and length >= 2). 
    497462private T[] toArray(T : T[]) (char[] src) { 
    498     T[] ret = new T[16];   // avoid unnecessary allocations 
     463    T[] ret = new T[16];    // avoid unnecessary allocations 
    499464    uint i = 0; 
    500465    foreach (char[] element; split(src[1..$-1])) { 
    501466        if (i == ret.length) ret.length = ret.length * 2; 
    502         ret[i] = parseTo!(T) (element); 
     467        ret[i] = deserialize!(T) (element); 
    503468        ++i; 
    504469    } 
     
    506471} 
    507472 
     473/** Set a struct's elements from an array. 
     474* 
     475* For a more generic version, see http://www.dsource.org/projects/tutorials/wiki/StructTupleof 
     476*/ 
     477// NOTE: Efficiency? Do recursive calls get inlined? 
     478private void setStruct(S, size_t N, size_t i = 0) (ref S s, char[][N] src) { 
     479    static assert (is(S == struct), "Only to be used with structs."); 
     480    static assert (N == S.tupleof.length, "src.length != S.tupleof.length"); 
     481    static if (i < N) { 
     482        if (src[i]) 
     483            s.tupleof[i] = deserialize!(typeof(s.tupleof[i])) (src[i]); 
     484        setStruct!(S, N, i+1) (s, src); 
     485    } 
     486} 
     487//END Utility funcs 
     488 
    508489debug (UnitTest) { 
    509     import tango.io.Console; 
    510      
    511     unittest { 
    512         Cout ("Running unittest: parseTo ...").flush; 
    513          
    514         assert (parseTo!(char[]) ("\"\\a\\b\\t\\n\\v\\f\\r\\\"\\\'\\\\\"") == "\a\b\t\n\v\f\r\"\'\\"); 
    515          
    516         Cout (" complete").newline; 
    517     } 
    518 
    519 //END Utility funcs 
     490    import tango.util.log.Log : Log, Logger; 
     491     
     492    private Logger logger; 
     493    static this() { 
     494        logger = Log.getLogger ("text.deserialize"); 
     495    } 
     496unittest { 
     497    // Utility 
     498    bool throws (void delegate() dg) { 
     499        bool r = false; 
     500        try { 
     501            dg(); 
     502        } catch (Exception e) { 
     503            r = true; 
     504            logger.info ("Exception caught: "~e.msg); 
     505        } 
     506        return r; 
     507    } 
     508    assert (!throws ({ int i = 5; })); 
     509    assert (throws ({ throw new Exception ("Test - this exception should be caught"); })); 
     510     
     511     
     512    // Associative arrays 
     513    char[][char] X = deserialize!(char[][char]) (`['a':"animal\n", 'b':['b','u','s','\n']]`); 
     514    char[][char] Y = ['a':cast(char[])"animal\n", 'b':['b','u','s','\n']]; 
     515     
     516    //FIXME: when the compiler's fixed: http://d.puremagic.com/issues/show_bug.cgi?id=1671 
     517    // just assert (X == Y) 
     518    assert (X.length == Y.length); 
     519    assert (X.keys == Y.keys); 
     520    assert (X.values == Y.values); 
     521    //X.rehash; Y.rehash;   // doesn't make a difference 
     522    //assert (X == Y);      // fails (compiler bug) 
     523     
     524    assert (throws ({ deserialize!(int[int]) (`[1:1`); }));             // bad brackets 
     525    assert (throws ({ deserialize!(int[char[]]) (`["ab\":1]`); }));     // unterminated quote 
     526    assert (throws ({ deserialize!(int[char[]]) (`["abc,\a\b\c":1]`); }));    // bad escape seq.&nbs