Mercurial > projects > dynamin
view dynamin/core/format.d @ 109:7678554e75de
Add dynamin.core.format with parseFormatString().
author | Jordan Miner <jminer7@gmail.com> |
---|---|
date | Sat, 19 Jan 2013 20:50:41 -0600 |
parents | |
children |
line wrap: on
line source
/* * Copyright Jordan Miner * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file BOOST_LICENSE.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) * */ module dynamin.core.format; import dynamin.core.global; import dynamin.core.string; import dynamin.core.test; private: cstring parseFormatParam(cstring specifier, ref int paramIndex) { // specifier is something like "1:x" or ":d" int i; for(i = 0; i < specifier.length && specifier[i] != ':'; ++i) { } if(i > 0) { try { paramIndex = to!int(specifier[0..i]); } catch(Exception ex) { throw new Exception("Invalid parameter index."); } } return i >= specifier.length ? "" : specifier[i+1..$]; } /** * Parses `formatStr`, passing each part to `write` or `writeParam` as it is encountered. * For example: * * parseFormatString("I am {0:x} years old!", writeParam, write); * // will result in: * write("I am "); * writeParam(0, "x"); * write(" years old!"); * */ public void parseFormatString( cstring formatStr, void delegate(int param, cstring format) writeParam, void delegate(scope cstring str) write) { int paramIndex = 0; // param to write next, if not specified cstring paramFormat; uword start = 0; for(int i = 0; i < formatStr.length; ++i) { if(formatStr[i] == '{') { write(formatStr[start..i]); start = i + 1; if(i + 1 >= formatStr.length) throw new Exception("Unterminated format specifier."); ++i; if(formatStr[i] == '{') { // escaped open brace // start already is the index of the second open brace } else { // format specifier while(formatStr[i] != '}') { if(formatStr[i] == '{') throw new Exception("Open brace in format specifier."); if(++i >= formatStr.length) throw new Exception("Unterminated format specifier."); } paramFormat = parseFormatParam(formatStr[start..i], paramIndex); start = i + 1; writeParam(paramIndex, paramFormat); ++paramIndex; } } else if(formatStr[i] == '}') { write(formatStr[start..i]); start = i + 1; if(i + 1 >= formatStr.length || formatStr[i + 1] != '}') throw new Exception("Unescaped close brace."); ++i; // escaped close brace } } if(start < formatStr.length) write(formatStr[start..$]); } unittest { struct Call { int index; string str; } Call[] calls; int callIndex; void paramCallback(int param, cstring format) { assertEqual(param, calls[callIndex].index); // crash if out of bounds assertEqual(format, calls[callIndex].str); ++callIndex; }; void strCallback(scope cstring str) { assertEqual(str, calls[callIndex].str); // crash if out of bounds ++callIndex; }; calls = [ Call(-1, "one"), Call(0, ""), Call(-1, "two"), Call(1, ""), Call(-1, "three") ]; callIndex = 0; parseFormatString("one{0}two{1}three", ¶mCallback, &strCallback); assertEqual(callIndex, calls.length); calls = [ Call(-1, "one"), Call(0, "abc12"), Call(-1, "two"), Call(1, "yyyy-mm-dd"), Call(-1, "three") ]; callIndex = 0; parseFormatString("one{0:abc12}two{1:yyyy-mm-dd}three", ¶mCallback, &strCallback); assertEqual(callIndex, calls.length); calls = [ Call(-1, "one"), Call(0, "~!@#$%^&*()_+-=[]\\:\"<>?;',./"), Call(-1, "two"), ]; callIndex = 0; parseFormatString("one{0:~!@#$%^&*()_+-=[]\\:\"<>?;',./}two", ¶mCallback, &strCallback); assertEqual(callIndex, calls.length); calls = [ Call(-1, "one"), Call(0, ""), Call(-1, "two"), Call(1, ""), Call(-1, "three"), Call(2, ""), ]; callIndex = 0; parseFormatString("one{}two{}three{}", ¶mCallback, &strCallback); assertEqual(callIndex, calls.length); calls = [ Call(-1, "one"), Call(4, ""), Call(-1, "two"), Call(5, ""), Call(-1, "three"), Call(2, ""), Call(-1, "four"), Call(3, "x"), ]; callIndex = 0; parseFormatString("one{4}two{}three{2}four{:x}", ¶mCallback, &strCallback); assertEqual(callIndex, calls.length); string testStr; parseFormatString( "on{{e}}{2}}}two{{{}}}", delegate (i, f) { testStr ~= "P"; }, delegate (scope str) { testStr ~= str; }); assertEqual(testStr, "on{e}P}two{P}"); assertThrows!Exception(parseFormatString( "test{", delegate (i, f) { }, delegate (scope str) { })); assertThrows!Exception(parseFormatString( "test{0", delegate (i, f) { }, delegate (scope str) { })); assertThrows!Exception(parseFormatString( "test{0{}", delegate (i, f) { }, delegate (scope str) { })); assertThrows!Exception(parseFormatString( "test}ing", delegate (i, f) { }, delegate (scope str) { })); assertThrows!Exception(parseFormatString( "test{0x12}ing", delegate (i, f) { }, delegate (scope str) { })); }