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;
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 
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 
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 }