comparison 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
comparison
equal deleted inserted replaced
108:b3091e460553 109:7678554e75de
1
2 /*
3 * Copyright Jordan Miner
4 *
5 * Distributed under the Boost Software License, Version 1.0.
6 * (See accompanying file BOOST_LICENSE.txt or copy at
7 * http://www.boost.org/LICENSE_1_0.txt)
8 *
9 */
10
11 module dynamin.core.format;
12
13 import dynamin.core.global;
14 import dynamin.core.string;
15 import dynamin.core.test;
16
17 private:
18
19 cstring parseFormatParam(cstring specifier, ref int paramIndex) {
20 // specifier is something like "1:x" or ":d"
21 int i;
22 for(i = 0; i < specifier.length && specifier[i] != ':'; ++i) {
23 }
24 if(i > 0) {
25 try {
26 paramIndex = to!int(specifier[0..i]);
27 } catch(Exception ex) {
28 throw new Exception("Invalid parameter index.");
29 }
30 }
31 return i >= specifier.length ? "" : specifier[i+1..$];
32 }
33
34 /**
35 * Parses `formatStr`, passing each part to `write` or `writeParam` as it is encountered.
36 * For example:
37 *
38 * parseFormatString("I am {0:x} years old!", writeParam, write);
39 * // will result in:
40 * write("I am ");
41 * writeParam(0, "x");
42 * write(" years old!");
43 *
44 */
45 public void parseFormatString( cstring formatStr,
46 void delegate(int param, cstring format) writeParam,
47 void delegate(scope cstring str) write) {
48
49 int paramIndex = 0; // param to write next, if not specified
50 cstring paramFormat;
51
52 uword start = 0;
53
54 for(int i = 0; i < formatStr.length; ++i) {
55 if(formatStr[i] == '{') {
56 write(formatStr[start..i]);
57 start = i + 1;
58
59 if(i + 1 >= formatStr.length)
60 throw new Exception("Unterminated format specifier.");
61 ++i;
62
63 if(formatStr[i] == '{') { // escaped open brace
64 // start already is the index of the second open brace
65 } else { // format specifier
66 while(formatStr[i] != '}') {
67 if(formatStr[i] == '{')
68 throw new Exception("Open brace in format specifier.");
69 if(++i >= formatStr.length)
70 throw new Exception("Unterminated format specifier.");
71 }
72 paramFormat = parseFormatParam(formatStr[start..i], paramIndex);
73 start = i + 1;
74 writeParam(paramIndex, paramFormat);
75 ++paramIndex;
76 }
77 } else if(formatStr[i] == '}') {
78 write(formatStr[start..i]);
79 start = i + 1;
80
81 if(i + 1 >= formatStr.length || formatStr[i + 1] != '}')
82 throw new Exception("Unescaped close brace.");
83 ++i; // escaped close brace
84 }
85 }
86 if(start < formatStr.length)
87 write(formatStr[start..$]);
88 }
89
90 unittest {
91 struct Call {
92 int index;
93 string str;
94 }
95 Call[] calls;
96 int callIndex;
97 void paramCallback(int param, cstring format) {
98 assertEqual(param, calls[callIndex].index); // crash if out of bounds
99 assertEqual(format, calls[callIndex].str);
100 ++callIndex;
101 };
102 void strCallback(scope cstring str) {
103 assertEqual(str, calls[callIndex].str); // crash if out of bounds
104 ++callIndex;
105 };
106
107 calls = [
108 Call(-1, "one"),
109 Call(0, ""),
110 Call(-1, "two"),
111 Call(1, ""),
112 Call(-1, "three")
113 ];
114 callIndex = 0;
115 parseFormatString("one{0}two{1}three", &paramCallback, &strCallback);
116 assertEqual(callIndex, calls.length);
117
118 calls = [
119 Call(-1, "one"),
120 Call(0, "abc12"),
121 Call(-1, "two"),
122 Call(1, "yyyy-mm-dd"),
123 Call(-1, "three")
124 ];
125 callIndex = 0;
126 parseFormatString("one{0:abc12}two{1:yyyy-mm-dd}three", &paramCallback, &strCallback);
127 assertEqual(callIndex, calls.length);
128
129 calls = [
130 Call(-1, "one"),
131 Call(0, "~!@#$%^&*()_+-=[]\\:\"<>?;',./"),
132 Call(-1, "two"),
133 ];
134 callIndex = 0;
135 parseFormatString("one{0:~!@#$%^&*()_+-=[]\\:\"<>?;',./}two", &paramCallback, &strCallback);
136 assertEqual(callIndex, calls.length);
137
138 calls = [
139 Call(-1, "one"),
140 Call(0, ""),
141 Call(-1, "two"),
142 Call(1, ""),
143 Call(-1, "three"),
144 Call(2, ""),
145 ];
146 callIndex = 0;
147 parseFormatString("one{}two{}three{}", &paramCallback, &strCallback);
148 assertEqual(callIndex, calls.length);
149
150 calls = [
151 Call(-1, "one"),
152 Call(4, ""),
153 Call(-1, "two"),
154 Call(5, ""),
155 Call(-1, "three"),
156 Call(2, ""),
157 Call(-1, "four"),
158 Call(3, "x"),
159 ];
160 callIndex = 0;
161 parseFormatString("one{4}two{}three{2}four{:x}", &paramCallback, &strCallback);
162 assertEqual(callIndex, calls.length);
163
164 string testStr;
165 parseFormatString( "on{{e}}{2}}}two{{{}}}",
166 delegate (i, f) { testStr ~= "P"; },
167 delegate (scope str) { testStr ~= str; });
168 assertEqual(testStr, "on{e}P}two{P}");
169
170 assertThrows!Exception(parseFormatString( "test{",
171 delegate (i, f) { },
172 delegate (scope str) { }));
173 assertThrows!Exception(parseFormatString( "test{0",
174 delegate (i, f) { },
175 delegate (scope str) { }));
176 assertThrows!Exception(parseFormatString( "test{0{}",
177 delegate (i, f) { },
178 delegate (scope str) { }));
179 assertThrows!Exception(parseFormatString( "test}ing",
180 delegate (i, f) { },
181 delegate (scope str) { }));
182 assertThrows!Exception(parseFormatString( "test{0x12}ing",
183 delegate (i, f) { },
184 delegate (scope str) { }));
185 }
186