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