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