# HG changeset patch # User Diggory Hardy # Date 1200487687 0 # Node ID b544c3a7c9cac1479773fab2ce623a4a7b3d80a1 # Parent dcb24afa0dcee61710de4469f9a1c1c1b3c2b76c Some changes to exceptions and a few more debug commands. committer: Diggory Hardy diff -r dcb24afa0dce -r b544c3a7c9ca conf/input.mtt --- a/conf/input.mtt Thu Jan 10 18:33:24 2008 +0000 +++ b/conf/input.mtt Wed Jan 16 12:48:07 2008 +0000 @@ -1,4 +1,4 @@ {MT01} - +! {0} diff -r dcb24afa0dce -r b544c3a7c9ca mde/exception.d --- a/mde/exception.d Thu Jan 10 18:33:24 2008 +0000 +++ b/mde/exception.d Wed Jan 16 12:48:07 2008 +0000 @@ -4,18 +4,18 @@ /** Base class for all mde Exceptions. * * All packages should have their own base exception type extending this one, and for each package - * level a CTOR taking a message should pass the message to the super prepended with "package: ". - * The performance of this is unimportant since exceptions are only intended for recovering from - * unexpected errors anyway. A CTOR not taking a message and calling the super without a parameter - * should also be provided. + * level a CTOR taking a message should pass the message to the super. The const string symbol + * should be overriden as below so that it ends up as something like "mde.file" or + * "mde.pkg.file.Class" describing where the exception was thrown. + * A CTOR not taking a message and calling the super without a parameter may also be provided. */ class mdeException : Exception { - const symbol = "mde"; /// Override in derived classes. + const symbol = "mde"; /// Override in derived classes to name the module where the error occured. this (char[] msg) { super(symbol ~ ": " ~ msg); } - this () { - super(""); // Exception doesn't have a this() CTOR + this () { // No supplied error message. + super(symbol); } } diff -r dcb24afa0dce -r b544c3a7c9ca mde/input/config.d --- a/mde/input/config.d Thu Jan 10 18:33:24 2008 +0000 +++ b/mde/input/config.d Wed Jan 16 12:48:07 2008 +0000 @@ -81,6 +81,7 @@ outQueue[uint] button; outQueue[uint] axis; /// ditto outQueue[uint] mouse; /// ditto + debug uint dnbc; // debug num button configs char[] name; /// Name for user to save this under. uint[] inheritants; /// Other profiles to inherit. @@ -124,7 +125,7 @@ char tmp[128] = void; logger.info (logger.format (tmp, "Loaded {} config sections.", configs.length)); foreach (id, cfg; configs) { - logger.trace ("Section "~format!(uint)(id)~": " ~ format!(uint[][uint])(cfg.button)); + logger.trace ("Section "~format!(uint)(id)~": " ~ format!(uint[][uint])(cfg.button) ~ " (" ~ format!(uint)(cfg.dnbc) ~ ")"); } } } @@ -137,6 +138,7 @@ if (id == QUEUE.BUTTON) { button = cast(outQueue[uint]) parse!(uint[][uint]) (dt); debug logger.trace ("Added button config: " ~ format!(uint[][uint])(button)); + debug ++dnbc; } else if (id == QUEUE.AXIS) axis = cast(outQueue[uint]) parse!(uint[][uint]) (dt); else if (id == QUEUE.MOUSE) mouse = cast(outQueue[uint]) parse!(uint[][uint]) (dt); @@ -150,5 +152,8 @@ void writeAll (ItemDelg) { // FIXME } + debug void debugFunc () { + logger.trace ("After parse: " ~ format!(uint[][uint])(button) ~ " (" ~ format!(uint)(dnbc) ~ ")"); + } //END File loading/saving code } diff -r dcb24afa0dce -r b544c3a7c9ca mde/input/exception.d --- a/mde/input/exception.d Thu Jan 10 18:33:24 2008 +0000 +++ b/mde/input/exception.d Wed Jan 16 12:48:07 2008 +0000 @@ -3,14 +3,26 @@ import mde.exception; /// Base Input exception class. -class InputException : mdeException { - const override symbol = super.symbol ~ ".input.input.Input"; +class inputException : mdeException { + const override symbol = super.symbol ~ ".input"; this (char[] msg) { super(msg); } this () {} } -class ConfigLoadException : InputException { +class InputClassException : inputException { + const override symbol = super.symbol ~ ".input.Input"; + this (char[] msg) { + super(msg); + } this () {} } + +class ConfigLoadException : inputException { + const override symbol = super.symbol ~ ".config.Config"; + this (char[] msg) { + super(msg); + } + this () {} +} diff -r dcb24afa0dce -r b544c3a7c9ca mde/input/input.d --- a/mde/input/input.d Thu Jan 10 18:33:24 2008 +0000 +++ b/mde/input/input.d Wed Jan 16 12:48:07 2008 +0000 @@ -205,7 +205,7 @@ } uint next () { // Get the next element. Throws an exception if there isn't another. if (p >= _q.length) - throw new InputException ("Input: Invalid configuration: incomplete config stack"); + throw new InputClassException ("Input: Invalid configuration: incomplete config stack"); uint ret = _q[p]; ++p; return ret; @@ -238,19 +238,19 @@ { ES_B_Func* func = (s.next() in es_b_fcts); if (func != null) (*func)(b,s); - else throw new InputException ("Input: Invalid configuration: bad event function code"); + else throw new InputClassException ("Input: Invalid configuration: bad event function code"); } void aEventOut (short x, readOutQueue s) { ES_A_Func* func = (s.next() in es_a_fcts); if (func != null) (*func)(x,s); - else throw new InputException ("Input: Invalid configuration: bad event function code"); + else throw new InputClassException ("Input: Invalid configuration: bad event function code"); } void mEventOut (short x, short y, readOutQueue s) { ES_M_Func* func = (s.next() in es_m_fcts); if (func != null) (*func)(x,y,s); - else throw new InputException ("Input: Invalid configuration: bad event function code"); + else throw new InputClassException ("Input: Invalid configuration: bad event function code"); } // The remaining functions are the stream functions, for adjusting and outputting an event. diff -r dcb24afa0dce -r b544c3a7c9ca mde/mergetag/dataset.d --- a/mde/mergetag/dataset.d Thu Jan 10 18:33:24 2008 +0000 +++ b/mde/mergetag/dataset.d Wed Jan 16 12:48:07 2008 +0000 @@ -8,7 +8,6 @@ import mde.text.util; import mde.text.parse : parse; import mde.text.format : format; -import mde.text.exception : TextParseException; // tango imports import Util = tango.text.Util; @@ -111,6 +110,7 @@ * calling addTag. */ void addTag (char[],ID,char[]); void writeAll (ItemDelg); /// TBD + debug void debugFunc (); /// Run in debug builds after parseSection. } /** @@ -253,6 +253,7 @@ void writeAll (ItemDelg itemdlg) { foreach (id, dt; _charA) itemdlg ("char[]", id, format!(char[])(dt)); } + debug void debugFunc () {} /* These make no attempt to check Arg is valid. * But if the symbol doesn't exist the complier will throw an error anyway, e.g.: diff -r dcb24afa0dce -r b544c3a7c9ca mde/mergetag/doc/issues.txt --- a/mde/mergetag/doc/issues.txt Thu Jan 10 18:33:24 2008 +0000 +++ b/mde/mergetag/doc/issues.txt Wed Jan 16 12:48:07 2008 +0000 @@ -2,6 +2,7 @@ Overall: Threading support? + Use string IDs instead of integers? Could actually be faster due to no conversion being necessary. read.d: Allow only partially loading a file. diff -r dcb24afa0dce -r b544c3a7c9ca mde/mergetag/exception.d --- a/mde/mergetag/exception.d Thu Jan 10 18:33:24 2008 +0000 +++ b/mde/mergetag/exception.d Wed Jan 16 12:48:07 2008 +0000 @@ -9,8 +9,9 @@ /// Base MergeTag exception class. class MTException : mdeException { + const override symbol = super.symbol ~ ".mergetag"; this (char[] msg) { - super("MergeTag: " ~ msg); + super(msg); } this () {} } diff -r dcb24afa0dce -r b544c3a7c9ca mde/mergetag/read.d --- a/mde/mergetag/read.d Thu Jan 10 18:33:24 2008 +0000 +++ b/mde/mergetag/read.d Wed Jan 16 12:48:07 2008 +0000 @@ -9,6 +9,7 @@ // package imports public import mde.mergetag.dataset; public import mde.mergetag.exception; +import mde.text.exception : textParseException; // tango imports import tango.io.UnicodeFile; @@ -79,6 +80,7 @@ static Logger logger; // Non-static symbols: + final char[] ErrFile; // added after ErrInFile to do the same without the "in " bit. final char[] ErrInFile; // something like "in \"path/file.mtt\"" final char[] fbuf; // file is read into this @@ -149,8 +151,9 @@ throwMTErr ("Error reading file: " ~ e.msg, new MTFileIOException); } // Remember the file name so that we can report errors (somewhat) informatively: - ErrInFile = " in \"" ~ path.path ~ path.file ~ '"'; - + ErrFile = path.path ~ path.file; + ErrInFile = " in \"" ~ ErrFile ~ '"'; + // Version checking & matching header section tag: if (fbuf.length < 6 || fbuf[0] != '{' || fbuf[1] != 'M' || fbuf[2] != 'T' || fbuf[5] != '}') throwMTErr("Not a valid MergeTag text file" ~ ErrInFile, new MTFileFormatException); @@ -220,6 +223,15 @@ read (hs); } public void read (View!(ID) secSet = new ArrayBag!(ID)) { /** ditto */ + /* Look for a section; return it if it exists otherwise create a new section: + * use dataSecCreator if it exists or just create a DefaultData if not. + */ + DataSection getOrCreateSec (ID id) { + DataSection* i = id in dataset.sec; + if (i) return *i; + return (dataset.sec[id] = (dataSecCreator !is null) ? dataSecCreator(id) : new DefaultData); + } + if (allRead || fatal) return; // never do anything in either case if (secSet.size) { if (secTable.length) { @@ -228,6 +240,7 @@ if (psmd && !psmd.read) { // may not exist DataSection ds = getOrCreateSec (id); parseSection (psmd.pos, &ds); + debug ds.debugFunc (); psmd.read = true; } } @@ -239,6 +252,7 @@ if (secSet.contains(id)) { DataSection ds = getOrCreateSec (id); pos = parseSection (pos, &ds); + debug ds.debugFunc (); secTable[id].read = true; } } catch (MTStringIDException) { // don't do any of the stuff above @@ -252,6 +266,7 @@ if (!smd.read) { DataSection ds = getOrCreateSec (id); parseSection (smd.pos, &ds); + debug ds.debugFunc (); smd.read = true; } } @@ -261,6 +276,7 @@ ID id = fbufReadSecMarker (pos); DataSection ds = getOrCreateSec (id); pos = parseSection (pos, &ds); + debug ds.debugFunc (); } catch (MTStringIDException) { pos = parseSection (pos, null); // just skip the section } @@ -281,6 +297,28 @@ slightly faster, but a tiny difference isn't worth the extra effort/risk of using char*'s. */ private uint parseSection (uint pos, DataSection* dsec) { + /* Searches fbuf starting from start to find one of <=>| and stops at its index. + + If quotable then be quote-aware for single and double quotes. + Note: there's no length restriction for the content of the quote since it could be a single + non-ascii UTF-8 char which would look like several chars. + */ + void fbufLocateDataTagChar (inout uint pos, bool quotable) { + for (; pos < fbuf.length; ++pos) { + if ((fbuf[pos] >= '<' && fbuf[pos] <= '>') || fbuf[pos] == '|') return; + else if (quotable) { + char c = fbuf[pos]; + if (c == '\'' || c == '"') { + ++pos; + while (fbuf[pos] != c) { + if (fbuf[pos] == '\\') ++pos; // escape seq. + fbufIncrement(pos); + } + } + } + } + } + bool comment = false; // preceding char was ! for (; pos < fbuf.length; ++pos) { if (Util.isSpace(fbuf[pos])) continue; // whitespace @@ -315,8 +353,9 @@ try { dsec.addTag (type, tagID, data); } - catch (TextParseException) { - logger.warn("Above error occured" ~ ErrInFile); // following a parse error + catch (textParseException e) { + logger.warn ("While reading " ~ ErrFile ~ ":"); // following a parse error + logger.warn (e.msg); } catch (MTUnknownTypeException e) { logger.warn ("Unsupported type \"" ~ type ~ "\" " ~ ErrInFile /*~ ":"*/); @@ -354,15 +393,6 @@ return pos; } - /* Look for a section; return it if it exists otherwise create a new section: - * use dataSecCreator if it exists or just create a DefaultData if not. - */ - DataSection getOrCreateSec (ID id) { - DataSection* i = id in dataset.sec; - if (i) return *i; - return (dataset.sec[id] = (dataSecCreator != null) ? dataSecCreator(id) : new DefaultData); - } - /* Parses fbuf for a section marker. Already knows fbuf[pos] == '{'. */ private ID fbufReadSecMarker (inout uint pos) { @@ -399,27 +429,6 @@ } } - /* Searches fbuf starting from start to find one of <=>| and stops at its index. - - If quotable then be quote-aware for single and double quotes. - Note: there's no length restriction for the content of the quote since it could be a single - non-ascii UTF-8 char which would look like several chars. - */ - private void fbufLocateDataTagChar (inout uint pos, bool quotable) { - for (; pos < fbuf.length; ++pos) { - if ((fbuf[pos] >= '<' && fbuf[pos] <= '>') || fbuf[pos] == '|') return; - else if (quotable) { - char c = fbuf[pos]; - if (c == '\'' || c == '"') { - ++pos; - while (fbuf[pos] != c) { - if (fbuf[pos] == '\\') ++pos; // escape seq. - fbufIncrement(pos); - } - } - } - } - } /* Increments pos and checks it hasn't hit fbuf.length . */ private void fbufIncrement(inout uint pos) { ++pos; diff -r dcb24afa0dce -r b544c3a7c9ca mde/text/exception.d --- a/mde/text/exception.d Thu Jan 10 18:33:24 2008 +0000 +++ b/mde/text/exception.d Wed Jan 16 12:48:07 2008 +0000 @@ -8,20 +8,29 @@ public import mde.exception; /// Base Text exception class. -class TextException : mdeException { +class textException : mdeException { + const override symbol = super.symbol ~ ".text"; this (char[] msg) { - super("Text: " ~ msg); + super(msg); } this () {} } /** Thrown by the parse module on any error. */ -class TextParseException : TextException { +class textParseException : textException { + const override symbol = super.symbol ~ ".parse"; + this (char[] msg) { + super(msg); + } this () {} } /** Thrown by the format module on any error. */ -class TextFormatException : TextException { -this () {} +class textFormatException : textException { + const override symbol = super.symbol ~ ".format"; + this (char[] msg) { + super(msg); + } + this () {} } diff -r dcb24afa0dce -r b544c3a7c9ca mde/text/format.d --- a/mde/text/format.d Thu Jan 10 18:33:24 2008 +0000 +++ b/mde/text/format.d Wed Jan 16 12:48:07 2008 +0000 @@ -29,12 +29,13 @@ import cFloat = tango.text.convert.Float; import Utf = tango.text.convert.Utf; import Util = tango.text.Util; +/+ import tango.util.log.Log : Log, Logger; private Logger logger; static this () { logger = Log.getLogger ("mde.text.format"); -} +}+/ //BEGIN Convert templates /* Idea: could extend format with a second parameter, containing flags for things like base to output. @@ -134,11 +135,11 @@ const char[] WIDE_CHAR_ERROR = "Error: unicode non-ascii character cannot be converted to a single UTF-8 char"; char[] format(T : dchar) (T val) { if (val <= 127u) return format (cast(char) val); // this char can be converted - throwException (WIDE_CHAR_ERROR); + throw new textFormatException (WIDE_CHAR_ERROR); } char[] format(T : wchar) (T val) { if (val <= 127u) return format (cast(char) val); // this char can be converted - throwException (WIDE_CHAR_ERROR); + throw new textFormatException (WIDE_CHAR_ERROR); } char[] format(T : char) (T val) { // Note: if (val > 127) "is invalid UTF-8 single char" @@ -192,7 +193,7 @@ return formatLong (val); } char[] format(T : ulong) (T val) { - if (val > cast(ulong) long.max) throwException ("No handling available for ulong where value > long.max"); + if (val > cast(ulong) long.max) throw new textFormatException ("No handling available for ulong where value > long.max"); return formatLong (val); } unittest { @@ -236,7 +237,7 @@ //BEGIN Utility funcs private char[] formatLong (long val) { try return cInt.toString (val, cInt.Style.Signed, cInt.Flags.Throw); - catch (Exception e) throwException (e.msg); + catch (Exception e) throw new textFormatException (e.msg); } private bool isEscapableChar (char c) { return ((c <= '\r' && c >= '\a') || c == '\"' || c == '\'' || c == '\\'); @@ -259,11 +260,6 @@ return escCharsRev[c]; } -private void throwException (char[] msg) { - logger.warn (msg); // only small errors are trapped here - throw new TextFormatException (); -} - unittest { // all utility functions should be well-enough used not to need testing } diff -r dcb24afa0dce -r b544c3a7c9ca mde/text/parse.d --- a/mde/text/parse.d Thu Jan 10 18:33:24 2008 +0000 +++ b/mde/text/parse.d Wed Jan 16 12:48:07 2008 +0000 @@ -20,7 +20,7 @@ * * There are also a few utility functions defined; the public ones have their own documentation. * - * On errors, a warning is logged and an TextParseException is thrown. No other exceptions should + * On errors, a textParseException is thrown with a suitable message. No other exceptions should * be thrown and none thrown from functions used outside this module. *************************************************************************************************/ module mde.text.parse; @@ -33,11 +33,13 @@ import cInt = tango.text.convert.Integer; import cFloat = tango.text.convert.Float; import Util = tango.text.Util; -import tango.util.log.Log : Log, Logger; +debug { + import tango.util.log.Log : Log, Logger; -private Logger logger; + private Logger logger; +} static this () { - logger = Log.getLogger ("mde.text.parse"); + debug logger = Log.getLogger ("mde.text.parse"); } //BEGIN parse templates @@ -46,7 +48,7 @@ T[S] parse(T : T[S], S) (char[] src) { src = Util.trim(src); if (src.length < 2 || src[0] != '[' || src[$-1] != ']') - throwException (AA_ERR ~ "not [ ... ]"); // bad braces. + throw new textParseException (AA_ERR ~ "not [ ... ]"); // bad braces. T[S] ret; foreach (char[] pair; split (src[1..$-1])) { @@ -58,20 +60,20 @@ ++i; while (i < pair.length && pair[i] != c) { if (pair[i] == '\\') { - if (i+2 >= pair.length) throwException (AA_ERR ~ "unfinished escape sequence within string/char"); + if (i+2 >= pair.length) throw new textParseException (AA_ERR ~ "unfinished escape sequence within string/char"); ++i; // escape seq. } ++i; } if (i == pair.length) { debug logger.warn ("Pair is: " ~ pair); - throwException (AA_ERR ~ "encountered [ ... KEY] (missing :DATA)"); + throw new textParseException (AA_ERR ~ "encountered [ ... KEY] (missing :DATA)"); } } ++i; } if (i == pair.length) { - throwException (AA_ERR ~ "encountered [ ... KEY:] (missing DATA)"); + throw new textParseException (AA_ERR ~ "encountered [ ... KEY:] (missing DATA)"); } ret[parse!(S) (pair[0..i])] = parse!(T) (pair[i+1..$]); } @@ -94,7 +96,7 @@ T[] parse(T : T[]) (char[] src) { src = Util.trim(src); if (src.length >= 2 && src[0] == '[' && src[$-1] == ']') return toArray!(T[]) (src); - throwException ("Invalid array: not [x, ..., z]"); + throw new textParseException ("Invalid array: not [x, ..., z]"); } T parse(T : char[]) (char[] src) { src = Util.trim(src); @@ -114,21 +116,21 @@ // process a block of escaped characters while (t < src.length && src[t] == '\\') { t++; - if (t == src.length) throwException ("Invalid string: ends \\\" !"); // next char is " + if (t == src.length) throw new textParseException ("Invalid string: ends \\\" !"); // next char is " ret[i++] = replaceEscapedChar (src[t++]); // throws if it's invalid } } return ret[0..i]; } else if (src.length >= 2 && src[0] == '[' && src[$-1] == ']') return toArray!(T) (src); - throwException ("Invalid string: not quoted (\"*\") or char array (['a',...,'c'])"); + throw new textParseException ("Invalid string: not quoted (\"*\") or char array (['a',...,'c'])"); } T parse(T : ubyte[]) (char[] src) { src = Util.trim(src); // Standard case: if (src.length >= 2 && src[0] == '[' && src[$-1] == ']') return toArray!(T) (src); // Special case: sequence of hex digits, each pair of which is a ubyte - if (src.length % 2 == 1) throwException ("Invalid binary: odd number of chars"); + if (src.length % 2 == 1) throw new textParseException ("Invalid binary: odd number of chars"); T ret; ret.length = src.length / 2; // exact for (uint i, pos; pos + 1 < src.length; ++i) { @@ -152,16 +154,16 @@ T parse(T : char) (char[] src) { src = Util.trim(src); if (src.length < 3 || src[0] != '\'' || src[$-1] != '\'') - throwException ("Invalid char: not quoted ('c')"); + throw new textParseException ("Invalid char: not quoted ('c')"); if (src[1] != '\\' && src.length == 3) return src[1]; // Either non escaped if (src.length == 4) return replaceEscapedChar (src[2]); // Or escaped // Report various errors; warnings for likely and difficult to tell cases: /+ This was caused by a bug. Shouldn't occur now normally. - if (src[1] == '\\' && src.length == 3) throwException (`Warning: \' in char! There's currently no support for this during tokenising. Thus your input's probably been garbled!`); // next char is ' +/ + if (src[1] == '\\' && src.length == 3) throw new textParseException (`Warning: \' in char! There's currently no support for this during tokenising. Thus your input's probably been garbled!`); // next char is ' +/ // Warn in case it's a multibyte UTF-8 character: - if (src[1] & 0xC0u) throwException ("Invalid char: too long (non-ASCII UTF-8 characters cannot be read as a single character)"); - throwException ("Invalid char: too long"); + if (src[1] & 0xC0u) throw new textParseException ("Invalid char: too long (non-ASCII UTF-8 characters cannot be read as a single character)"); + throw new textParseException ("Invalid char: too long"); } // unittest covered above @@ -173,7 +175,7 @@ while (src.length > pos && src[pos] == '0') ++pos; // strip leading zeros if (src.length == pos && pos > 0) return false; if (src.length == pos + 1 && src[pos] == '1') return true; - throwException ("Invalid bool: not true or false and doesn't evaluate to 0 or 1"); + throw new textParseException ("Invalid bool: not true or false and doesn't evaluate to 0 or 1"); } unittest { assert (parse!(bool[]) (`[true,false,01,00]`) == cast(bool[]) [1,0,1,0]); @@ -236,20 +238,20 @@ uint radix, ate, ate2; ate = cInt.trim (src, sign, radix); - if (ate == src.length) throwException ("Invalid integer: no digits"); + if (ate == src.length) throw new textParseException ("Invalid integer: no digits"); ulong val = cInt.convert (src[ate..$], radix, &ate2); ate += ate2; while (ate < src.length) { if (src[ate] == ' ' || src[ate] == '\t') ++ate; - else throwException ("Invalid integer"); + else throw new textParseException ("Invalid integer"); } - if (val > TInt.max) throwException (INT_OUT_OF_RANGE); + if (val > TInt.max) throw new textParseException (INT_OUT_OF_RANGE); if (sign) { long sval = cast(long) -val; if (sval > TInt.min) return cast(TInt) sval; - else throwException (INT_OUT_OF_RANGE); + else throw new textParseException (INT_OUT_OF_RANGE); } return cast(TInt) val; } @@ -259,7 +261,7 @@ * when it does. */ TFloat toTFloat(TFloat) (char[] src) { src = postTrim (src); - if (src == "") throwException ("Invalid float: no digits"); + if (src == "") throw new textParseException ("Invalid float: no digits"); uint ate; TFloat x = cFloat.parse (src, &ate); @@ -292,7 +294,7 @@ else if (c == '[') ++depth; else if (c == ']') { if (depth) --depth; - else throwException ("Invalid array literal: closes before end of data item."); + else throw new textParseException ("Invalid array literal: closes before end of data item."); } else if (c == ',' && depth == 0) { // only if not an embedded array if (ret.length <= k) ret.length = ret.length * 2; @@ -327,7 +329,7 @@ char* r = c in escChars; if (r != null) return *r; - throwException ("Invalid escape sequence: \\"~c); // we didn't return, so something failed + throw new textParseException ("Invalid escape sequence: \\"~c); // we didn't return, so something failed } // Reads one hex char: [0-9A-Fa-f]. Otherwise throws an exception. Doesn't check src.length. @@ -336,7 +338,7 @@ if (src[pos] >= '0' && src[pos] <= '9') x = src[pos] - '0'; else if (src[pos] >= 'A' && src[pos] <= 'F') x = src[pos] - 'A' + 10; else if (src[pos] >= 'a' && src[pos] <= 'f') x = src[pos] - 'a' + 10; - else throwException ("Invalid hex digit."); + else throw new textParseException ("Invalid hex digit."); ++pos; return x; } @@ -354,11 +356,6 @@ return ret[0..i]; } -private void throwException (char[] msg) { - logger.warn (msg); // only small errors are trapped here - throw new TextParseException (); -} - unittest { // all utility functions should be well-enough used not to need testing }