1 // Written in the D programming language.
2
3 /*
4 Copyright: Copyright The D Language Foundation 2000-2013.
5
6 License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0).
7
8 Authors: $(HTTP walterbright.com, Walter Bright), $(HTTP erdani.com,
9 Andrei Alexandrescu), and Kenji Hara
10
11 Source: $(PHOBOSSRC std/format/internal/read.d)
12 */
13 module std.format.internal.read;
14
15 import std.range.primitives : ElementEncodingType, ElementType, isInputRange;
16
17 import std.traits : isAggregateType, isArray, isAssociativeArray,
18 isDynamicArray, isFloatingPoint, isIntegral, isSomeChar, isSomeString,
19 isStaticArray, StringTypeOf;
20
21 import std.format.spec : FormatSpec;
22
23 package(std.format):
24
skipData(Range,Char)25 void skipData(Range, Char)(ref Range input, scope const ref FormatSpec!Char spec)
26 {
27 import std.ascii : isDigit;
28 import std.conv : text;
29 import std.range.primitives : empty, front, popFront;
30
31 switch (spec.spec)
32 {
33 case 'c': input.popFront(); break;
34 case 'd':
35 if (input.front == '+' || input.front == '-') input.popFront();
36 goto case 'u';
37 case 'u':
38 while (!input.empty && isDigit(input.front)) input.popFront();
39 break;
40 default:
41 assert(false,
42 text("Format specifier not understood: %", spec.spec));
43 }
44 }
45
acceptedSpecs(T)46 private template acceptedSpecs(T)
47 {
48 static if (isIntegral!T)
49 enum acceptedSpecs = "bdosuxX";
50 else static if (isFloatingPoint!T)
51 enum acceptedSpecs = "seEfgG";
52 else static if (isSomeChar!T)
53 enum acceptedSpecs = "bcdosuxX"; // integral + 'c'
54 else
55 enum acceptedSpecs = "";
56 }
57
58 T unformatValueImpl(T, Range, Char)(ref Range input, scope const ref FormatSpec!Char spec)
59 if (isInputRange!Range && is(immutable T == immutable bool))
60 {
61 import std.algorithm.searching : find;
62 import std.conv : parse, text;
63 import std.format : enforceFmt, unformatValue;
64
65 if (spec.spec == 's') return parse!T(input);
66
67 enforceFmt(find(acceptedSpecs!long, spec.spec).length,
68 text("Wrong unformat specifier '%", spec.spec , "' for ", T.stringof));
69
70 return unformatValue!long(input, spec) != 0;
71 }
72
73 T unformatValueImpl(T, Range, Char)(ref Range input, scope const ref FormatSpec!Char spec)
74 if (isInputRange!Range && is(T == typeof(null)))
75 {
76 import std.conv : parse, text;
77 import std.format : enforceFmt;
78
79 enforceFmt(spec.spec == 's',
80 text("Wrong unformat specifier '%", spec.spec , "' for ", T.stringof));
81
82 return parse!T(input);
83 }
84
85 T unformatValueImpl(T, Range, Char)(ref Range input, scope const ref FormatSpec!Char spec)
86 if (isInputRange!Range && isIntegral!T && !is(T == enum) && isSomeChar!(ElementType!Range))
87 {
88 import std.algorithm.searching : find;
89 import std.conv : parse, text;
90 import std.format : enforceFmt, FormatException;
91
92 if (spec.spec == 'r')
93 {
94 static if (is(immutable ElementEncodingType!Range == immutable char)
95 || is(immutable ElementEncodingType!Range == immutable byte)
96 || is(immutable ElementEncodingType!Range == immutable ubyte))
97 return rawRead!T(input);
98 else
99 throw new FormatException(
100 "The raw read specifier %r may only be used with narrow strings and ranges of bytes."
101 );
102 }
103
104 enforceFmt(find(acceptedSpecs!T, spec.spec).length,
105 text("Wrong unformat specifier '%", spec.spec , "' for ", T.stringof));
106
107 enforceFmt(spec.width == 0, "Parsing integers with a width specification is not implemented"); // TODO
108
109 immutable uint base =
110 spec.spec == 'x' || spec.spec == 'X' ? 16 :
111 spec.spec == 'o' ? 8 :
112 spec.spec == 'b' ? 2 :
113 spec.spec == 's' || spec.spec == 'd' || spec.spec == 'u' ? 10 : 0;
114 assert(base != 0, "base must be not equal to zero");
115
116 return parse!T(input, base);
117
118 }
119
120 T unformatValueImpl(T, Range, Char)(ref Range input, scope const ref FormatSpec!Char spec)
121 if (isFloatingPoint!T && !is(T == enum) && isInputRange!Range
122 && isSomeChar!(ElementType!Range)&& !is(Range == enum))
123 {
124 import std.algorithm.searching : find;
125 import std.conv : parse, text;
126 import std.format : enforceFmt, FormatException;
127
128 if (spec.spec == 'r')
129 {
130 static if (is(immutable ElementEncodingType!Range == immutable char)
131 || is(immutable ElementEncodingType!Range == immutable byte)
132 || is(immutable ElementEncodingType!Range == immutable ubyte))
133 return rawRead!T(input);
134 else
135 throw new FormatException(
136 "The raw read specifier %r may only be used with narrow strings and ranges of bytes."
137 );
138 }
139
140 enforceFmt(find(acceptedSpecs!T, spec.spec).length,
141 text("Wrong unformat specifier '%", spec.spec , "' for ", T.stringof));
142
143 return parse!T(input);
144 }
145
146 T unformatValueImpl(T, Range, Char)(ref Range input, scope const ref FormatSpec!Char spec)
147 if (isInputRange!Range && isSomeChar!T && !is(T == enum) && isSomeChar!(ElementType!Range))
148 {
149 import std.algorithm.searching : find;
150 import std.conv : to, text;
151 import std.range.primitives : empty, front, popFront;
152 import std.format : enforceFmt, unformatValue;
153
154 if (spec.spec == 's' || spec.spec == 'c')
155 {
156 auto result = to!T(input.front);
157 input.popFront();
158 return result;
159 }
160
161 enforceFmt(find(acceptedSpecs!T, spec.spec).length,
162 text("Wrong unformat specifier '%", spec.spec , "' for ", T.stringof));
163
164 static if (T.sizeof == 1)
165 return unformatValue!ubyte(input, spec);
166 else static if (T.sizeof == 2)
167 return unformatValue!ushort(input, spec);
168 else static if (T.sizeof == 4)
169 return unformatValue!uint(input, spec);
170 else
171 static assert(false, T.stringof ~ ".sizeof must be 1, 2, or 4 not " ~
172 to!string(T.sizeof));
173 }
174
175 T unformatValueImpl(T, Range, Char)(ref Range input, scope const ref FormatSpec!Char fmt)
176 if (isInputRange!Range && is(StringTypeOf!T) && !isAggregateType!T && !is(T == enum))
177 {
178 import std.conv : text;
179 import std.range.primitives : empty, front, popFront, put;
180 import std.format : enforceFmt;
181
182 const spec = fmt.spec;
183 if (spec == '(')
184 {
185 return unformatRange!T(input, fmt);
186 }
187 enforceFmt(spec == 's',
188 text("Wrong unformat specifier '%", spec , "' for ", T.stringof));
189
190 static if (isStaticArray!T)
191 {
192 T result;
193 auto app = result[];
194 }
195 else
196 {
197 import std.array : appender;
198 auto app = appender!T();
199 }
200 if (fmt.trailing.empty)
201 {
202 for (; !input.empty; input.popFront())
203 {
204 static if (isStaticArray!T)
205 if (app.empty)
206 break;
207 app.put(input.front);
208 }
209 }
210 else
211 {
212 immutable end = fmt.trailing.front;
213 for (; !input.empty && input.front != end; input.popFront())
214 {
215 static if (isStaticArray!T)
216 if (app.empty)
217 break;
218 app.put(input.front);
219 }
220 }
221 static if (isStaticArray!T)
222 {
223 enforceFmt(app.empty, "need more input");
224 return result;
225 }
226 else
227 return app.data;
228 }
229
230 T unformatValueImpl(T, Range, Char)(ref Range input, scope const ref FormatSpec!Char fmt)
231 if (isInputRange!Range && !is(StringTypeOf!T) && !isAggregateType!T
232 && (isArray!T || isAssociativeArray!T || is(T == enum)))
233 {
234 import std.conv : parse, text;
235 import std.format : enforceFmt;
236
237 const spec = fmt.spec;
238 if (spec == '(')
239 {
240 return unformatRange!T(input, fmt);
241 }
242
243 enforceFmt(spec == 's',
244 text("Wrong unformat specifier '%", spec , "' for ", T.stringof));
245
246 return parse!T(input);
247 }
248
249 /*
250 * Function that performs raw reading. Used by unformatValue
251 * for integral and float types.
252 */
253 private T rawRead(T, Range)(ref Range input)
254 if (is(immutable ElementEncodingType!Range == immutable char)
255 || is(immutable ElementEncodingType!Range == immutable byte)
256 || is(immutable ElementEncodingType!Range == immutable ubyte))
257 {
258 import std.range.primitives : popFront;
259
260 union X
261 {
262 ubyte[T.sizeof] raw;
263 T typed;
264 }
265 X x;
266 foreach (i; 0 .. T.sizeof)
267 {
268 static if (isSomeString!Range)
269 {
270 x.raw[i] = input[0];
271 input = input[1 .. $];
272 }
273 else
274 {
275 // TODO: recheck this
276 x.raw[i] = input.front;
277 input.popFront();
278 }
279 }
280 return x.typed;
281 }
282
unformatRange(T,Range,Char)283 private T unformatRange(T, Range, Char)(ref Range input, scope const ref FormatSpec!Char spec)
284 in (spec.spec == '(', "spec.spec must be '(' not " ~ spec.spec)
285 {
286 import std.range.primitives : empty, front, popFront;
287 import std.format : enforceFmt, format;
288
289 T result;
290 static if (isStaticArray!T)
291 {
292 size_t i;
293 }
294
295 const(Char)[] cont = spec.trailing;
296 for (size_t j = 0; j < spec.trailing.length; ++j)
297 {
298 if (spec.trailing[j] == '%')
299 {
300 cont = spec.trailing[0 .. j];
301 break;
302 }
303 }
304
305 bool checkEnd()
306 {
307 return input.empty || !cont.empty && input.front == cont.front;
308 }
309
310 if (!checkEnd())
311 {
312 for (;;)
313 {
314 auto fmt = FormatSpec!Char(spec.nested);
315 fmt.readUpToNextSpec(input);
316 enforceFmt(!input.empty, "Unexpected end of input when parsing range");
317
318 static if (isStaticArray!T)
319 {
320 result[i++] = unformatElement!(typeof(T.init[0]))(input, fmt);
321 }
322 else static if (isDynamicArray!T)
323 {
324 import std.conv : WideElementType;
325 result ~= unformatElement!(WideElementType!T)(input, fmt);
326 }
327 else static if (isAssociativeArray!T)
328 {
329 auto key = unformatElement!(typeof(T.init.keys[0]))(input, fmt);
330 fmt.readUpToNextSpec(input); // eat key separator
331
332 result[key] = unformatElement!(typeof(T.init.values[0]))(input, fmt);
333 }
334
335 static if (isStaticArray!T)
336 {
337 enforceFmt(i <= T.length,
338 "Too many format specifiers for static array of length %d".format(T.length));
339 }
340
341 if (spec.sep !is null)
342 fmt.readUpToNextSpec(input);
343 auto sep = spec.sep !is null ? spec.sep : fmt.trailing;
344
345 if (checkEnd())
346 break;
347
348 if (!sep.empty && input.front == sep.front)
349 {
350 while (!sep.empty)
351 {
352 enforceFmt(!input.empty,
353 "Unexpected end of input when parsing range separator");
354 enforceFmt(input.front == sep.front,
355 "Unexpected character when parsing range separator");
356 input.popFront();
357 sep.popFront();
358 }
359 }
360 }
361 }
362 static if (isStaticArray!T)
363 {
364 enforceFmt(i == T.length,
365 "Too few (%d) format specifiers for static array of length %d".format(i, T.length));
366 }
367 return result;
368 }
369
370 T unformatElement(T, Range, Char)(ref Range input, scope const ref FormatSpec!Char spec)
371 if (isInputRange!Range)
372 {
373 import std.conv : parseElement;
374 import std.format.read : unformatValue;
375
376 static if (isSomeString!T)
377 {
378 if (spec.spec == 's')
379 {
380 return parseElement!T(input);
381 }
382 }
383 else static if (isSomeChar!T)
384 {
385 if (spec.spec == 's')
386 {
387 return parseElement!T(input);
388 }
389 }
390
391 return unformatValue!T(input, spec);
392 }
393