The OpenD Programming Language

1 // Written in the D programming language.
2 
3 /**
4 This is a submodule of $(MREF std, format).
5 
6 It provides two functions for reading formatted input: $(LREF
7 unformatValue) and $(LREF formattedRead). The former reads a single
8 value. The latter reads several values at once and matches the
9 characters found between format specifiers.
10 
11 Parameters are ignored, except for the ones consisting of a single
12 $(B '*'). See $(LREF formattedRead) for more information.
13 
14 A space outside of a format specifier has a special meaning: it
15 matches any sequence of whitespace characters, not just a single
16 space.
17 
18 The following combinations of format characters and types are
19 available:
20 
21 $(BOOKTABLE ,
22 $(TR $(TH) $(TH s) $(TH c) $(TH d, u, b, o, x, X) $(TH e, E, f, g, G) $(TH r) $(TH compound))
23 $(TR $(TD `bool`) $(TD yes) $(TD $(MDASH)) $(TD yes) $(TD $(MDASH)) $(TD $(MDASH)) $(TD $(MDASH)))
24 $(TR $(TD `null`) $(TD yes) $(TD $(MDASH)) $(TD $(MDASH)) $(TD $(MDASH)) $(TD $(MDASH)) $(TD $(MDASH)))
25 $(TR $(TD $(I integer)) $(TD yes) $(TD $(MDASH)) $(TD yes) $(TD $(MDASH)) $(TD yes) $(TD $(MDASH)))
26 $(TR $(TD $(I floating point)) $(TD yes) $(TD $(MDASH)) $(TD $(MDASH)) $(TD yes) $(TD yes) $(TD $(MDASH)))
27 $(TR $(TD $(I character)) $(TD yes) $(TD yes) $(TD yes) $(TD $(MDASH)) $(TD $(MDASH)) $(TD $(MDASH)))
28 $(TR $(TD $(I string)) $(TD yes) $(TD $(MDASH)) $(TD $(MDASH)) $(TD $(MDASH)) $(TD $(MDASH)) $(TD yes))
29 $(TR $(TD $(I array)) $(TD yes) $(TD $(MDASH)) $(TD $(MDASH)) $(TD $(MDASH)) $(TD $(MDASH)) $(TD yes))
30 $(TR $(TD $(I associative array)) $(TD yes) $(TD $(MDASH)) $(TD $(MDASH)) $(TD $(MDASH)) $(TD $(MDASH)) $(TD yes))
31 )
32 
33 Below are highlighted examples on how these combinations are used
34 with $(LREF unformatValue), however, they apply for $(LREF
35 formattedRead) also
36 
37 Copyright: Copyright The D Language Foundation 2000-2013.
38 
39 License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0).
40 
41 Authors: $(HTTP walterbright.com, Walter Bright), $(HTTP erdani.com,
42 Andrei Alexandrescu), and Kenji Hara
43 
44 Source: $(PHOBOSSRC std/format/read.d)
45  */
46 module std.format.read;
47 
48 /// Booleans
49 @safe pure unittest
50 {
51     import std.format.spec : singleSpec;
52 
53     auto str = "false";
54     auto spec = singleSpec("%s");
55     assert(str.unformatValue!bool(spec) == false);
56 
57     str = "1";
58     spec = singleSpec("%d");
59     assert(str.unformatValue!bool(spec) == true);
60 }
61 
62 /// Null values
63 @safe pure unittest
64 {
65     import std.format.spec : singleSpec;
66 
67     auto str = "null";
68     auto spec = singleSpec("%s");
69     assert(str.unformatValue!(typeof(null))(spec) == null);
70 }
71 
72 /// Integrals
73 @safe pure unittest
74 {
75     import std.format.spec : singleSpec;
76 
77     // signed decimal values
78     auto str = "123";
79     auto spec = singleSpec("%s");
80     assert(str.unformatValue!int(spec) == 123);
81 
82     // hexadecimal values
83     str = "ABC";
84     spec = singleSpec("%X");
85     assert(str.unformatValue!int(spec) == 2748);
86 
87     // octal values
88     str = "11610";
89     spec = singleSpec("%o");
90     assert(str.unformatValue!int(spec) == 5000);
91 
92     // raw read, depends on endianess
93     str = "\x75\x01";
94     spec = singleSpec("%r");
95     auto result = str.unformatValue!short(spec);
96     assert(result == 373 /* little endian */ || result == 29953 /* big endian */ );
97 }
98 
99 /// Floating point numbers
100 @safe pure unittest
101 {
102     import std.format.spec : singleSpec;
103     import std.math.operations : isClose;
104 
105     // natural notation
106     auto str = "123.456";
107     auto spec = singleSpec("%s");
108     assert(str.unformatValue!double(spec).isClose(123.456));
109 
110     // scientific notation
111     str = "1e17";
112     spec = singleSpec("%e");
113     assert(str.unformatValue!double(spec).isClose(1e17));
114 
115     // raw read, depends on endianess
116     str = "\x40\x00\x00\xBF";
117     spec = singleSpec("%r");
118     auto result = str.unformatValue!float(spec);
119     assert(isClose(result, -0.5) /* little endian */ || isClose(result, 2.0) /* big endian */ );
120 }
121 
122 /// Characters
123 @safe pure unittest
124 {
125     import std.format.spec : singleSpec;
126 
127     // only the first character is read
128     auto str = "abc";
129     auto spec = singleSpec("%s");
130     assert(str.unformatValue!char(spec) == 'a');
131 
132     // using a numerical format character treats the read number as unicode code point
133     str = "65";
134     spec = singleSpec("%d");
135     assert(str.unformatValue!char(spec) == 'A');
136 
137     str = "41";
138     spec = singleSpec("%x");
139     assert(str.unformatValue!char(spec) == 'A');
140 
141     str = "10003";
142     spec = singleSpec("%d");
143     assert(str.unformatValue!dchar(spec) == '✓');
144 }
145 
146 /// Arrays
147 @safe pure unittest
148 {
149     import std.format.spec : singleSpec;
150 
151     // string value
152     string str = "aaa";
153     auto spec = singleSpec("%s");
154     assert(str.unformatValue!(dchar[])(spec) == "aaa"d);
155 
156     // fixed size array with characters
157     str = "aaa";
158     spec = singleSpec("%s");
159     dchar[3] ret = ['a', 'a', 'a'];
160     assert(str.unformatValue!(dchar[3])(spec) == ret);
161 
162     // dynamic array
163     str = "[1, 2, 3, 4]";
164     spec = singleSpec("%s");
165     assert(str.unformatValue!(int[])(spec) == [1, 2, 3, 4]);
166 
167     // fixed size array with integers
168     str = "[1, 2, 3, 4]";
169     spec = singleSpec("%s");
170     int[4] ret2 = [1, 2, 3, 4];
171     assert(str.unformatValue!(int[4])(spec) == ret2);
172 
173     // compound specifiers can be used for more control
174     str = "1,2,3";
175     spec = singleSpec("%(%s,%)");
176     assert(str.unformatValue!(int[])(spec) == [1, 2, 3]);
177 
178     str = "cool";
179     spec = singleSpec("%(%c%)");
180     assert(str.unformatValue!(char[])(spec) == ['c', 'o', 'o', 'l']);
181 }
182 
183 /// Associative arrays
184 @safe pure unittest
185 {
186     import std.format.spec : singleSpec;
187 
188     // as single value
189     auto str = `["one": 1, "two": 2]`;
190     auto spec = singleSpec("%s");
191     assert(str.unformatValue!(int[string])(spec) == ["one": 1, "two": 2]);
192 
193     // with compound specifier for more control
194     str = "1/1, 2/4, 3/9";
195     spec = singleSpec("%(%d/%d%|, %)");
196     assert(str.unformatValue!(int[int])(spec) == [1: 1, 2: 4, 3: 9]);
197 }
198 
199 import std.format.spec : FormatSpec;
200 import std.format.internal.read;
201 import std.traits : isSomeString;
202 
203 /**
204 Reads an input range according to a format string and stores the read
205 values into its arguments.
206 
207 Format specifiers with format character $(B 'd'), $(B 'u') and $(B
208 'c') can take a $(B '*') parameter for skipping values.
209 
210 The second version of `formattedRead` takes the format string as
211 template argument. In this case, it is checked for consistency at
212 compile-time.
213 
214 Params:
215     r = an $(REF_ALTTEXT input range, isInputRange, std, range, primitives),
216         where the formatted input is read from
217     fmt = a $(MREF_ALTTEXT format string, std,format)
218     args = a variadic list of arguments where the read values are stored
219     Range = the type of the input range `r`
220     Char = the character type used for `fmt`
221     Args = a variadic list of types of the arguments
222 
223 Returns:
224     The number of variables filled. If the input range `r` ends early,
225     this number will be less than the number of variables provided.
226 
227 Throws:
228     A $(REF_ALTTEXT FormatException, FormatException, std, format)
229     if reading did not succeed.
230 
231 Note:
232     For backward compatibility the arguments `args` can be given as pointers
233     to that variable, but it is not recommended to do so, because this
234     option might be removed in the future.
235  */
236 uint formattedRead(Range, Char, Args...)(auto ref Range r, const(Char)[] fmt, auto ref Args args)
237 {
238     import std.format : enforceFmt;
239     import std.range.primitives : empty;
240     import std.traits : isPointer;
241     import std.typecons : isTuple;
242 
243     auto spec = FormatSpec!Char(fmt);
244     static if (!Args.length)
245     {
246         spec.readUpToNextSpec(r);
247         enforceFmt(spec.trailing.empty, "Trailing characters in formattedRead format string");
248         return 0;
249     }
250     else
251     {
252         enum hasPointer = isPointer!(typeof(args[0]));
253 
254         // The function below accounts for '*' == fields meant to be
255         // read and skipped
256         void skipUnstoredFields()
257         {
258             for (;;)
259             {
260                 spec.readUpToNextSpec(r);
261                 if (spec.width != spec.DYNAMIC) break;
262                 // must skip this field
263                 skipData(r, spec);
264             }
265         }
266 
267         skipUnstoredFields();
268         if (r.empty)
269         {
270             // Input is empty, nothing to read
271             return 0;
272         }
273 
274         static if (hasPointer)
275             alias A = typeof(*args[0]);
276         else
277             alias A = typeof(args[0]);
278 
279         static if (isTuple!A)
280         {
281             foreach (i, T; A.Types)
282             {
283                 static if (hasPointer)
284                     (*args[0])[i] = unformatValue!(T)(r, spec);
285                 else
286                     args[0][i] = unformatValue!(T)(r, spec);
287                 skipUnstoredFields();
288             }
289         }
290         else
291         {
292             static if (hasPointer)
293                 *args[0] = unformatValue!(A)(r, spec);
294             else
295                 args[0] = unformatValue!(A)(r, spec);
296         }
297         return 1 + formattedRead(r, spec.trailing, args[1 .. $]);
298     }
299 }
300 
301 /// ditto
302 uint formattedRead(alias fmt, Range, Args...)(auto ref Range r, auto ref Args args)
303 if (isSomeString!(typeof(fmt)))
304 {
305     import std.format : checkFormatException;
306     import std.meta : staticMap;
307     import std.typecons : Tuple;
308 
309 
310     // formattedRead supports std.typecons.Tuple
311     // however, checkFormatException does not
312     // this means that all std.typecons.Tuple's types in Args must be unwrapped
313     // and passed to checkFormatException
314     template Flatten(T)
315     {
316         static if (is(T : Tuple!Args, Args...))
317             alias Flatten = Args;
318         else
319             alias Flatten = T;
320     }
321 
322     alias e = checkFormatException!(fmt, staticMap!(Flatten, Args));
323     static assert(!e, e);
324     return .formattedRead(r, fmt, args);
325 }
326 
327 ///
328 @safe pure unittest
329 {
330     string object;
331     char cmp;
332     int value;
333 
334     assert(formattedRead("angle < 36", "%s %c %d", object, cmp, value) == 3);
335     assert(object == "angle");
336     assert(cmp == '<');
337     assert(value == 36);
338 
339     // reading may end early:
340     assert(formattedRead("length >", "%s %c %d", object, cmp, value) == 2);
341     assert(object == "length");
342     assert(cmp == '>');
343     // value is not changed:
344     assert(value == 36);
345 }
346 
347 /// The format string can be checked at compile-time:
348 @safe pure unittest
349 {
350     string a;
351     int b;
352     double c;
353 
354     assert("hello!124:34.5".formattedRead!"%s!%s:%s"(a, b, c) == 3);
355     assert(a == "hello");
356     assert(b == 124);
357     assert(c == 34.5);
358 }
359 
360 /// Skipping values
361 @safe pure unittest
362 {
363     string item;
364     double amount;
365 
366     assert("orange: (12%) 15.25".formattedRead("%s: (%*d%%) %f", item, amount) == 2);
367     assert(item == "orange");
368     assert(amount == 15.25);
369 
370     // can also be used with tuples
371     import std.typecons : Tuple;
372 
373     Tuple!(int, float) t;
374     char[] line = "1 7643 2.125".dup;
375     formattedRead(line, "%s %*u %s", t);
376     assert(t[0] == 1 && t[1] == 2.125);
377 }
378 
379 // https://issues.dlang.org/show_bug.cgi?id=23600
380 @safe pure unittest
381 {
382     import std.typecons : Tuple, tuple;
383 
384     string h, w;
385     Tuple!(int, float) t;
386 
387     assert("hello 1 2.34 world".formattedRead!"%s %d %f %s"(h, t, w) == 3);
388     assert(h == "hello");
389     assert(t == tuple(1, 2.34f));
390     assert(w == "world");
391 }
392 
393 @safe unittest
394 {
395     import std.math.operations : isClose;
396     import std.math.traits : isNaN;
397     import std.range.primitives : empty;
398 
399     string s = " 1.2 3.4 ";
400     double x, y, z;
401     assert(formattedRead(s, " %s %s %s ", x, y, z) == 2);
402     assert(s.empty);
403     assert(isClose(x, 1.2));
404     assert(isClose(y, 3.4));
405     assert(isNaN(z));
406 }
407 
408 // for backwards compatibility
409 @safe pure unittest
410 {
411     string s = "hello!124:34.5";
412     string a;
413     int b;
414     double c;
415     formattedRead(s, "%s!%s:%s", &a, &b, &c);
416     assert(a == "hello" && b == 124 && c == 34.5);
417 
418     // mix pointers and auto-ref
419     s = "world!200:42.25";
420     formattedRead(s, "%s!%s:%s", a, &b, &c);
421     assert(a == "world" && b == 200 && c == 42.25);
422 
423     s = "world1!201:42.5";
424     formattedRead(s, "%s!%s:%s", &a, &b, c);
425     assert(a == "world1" && b == 201 && c == 42.5);
426 
427     s = "world2!202:42.75";
428     formattedRead(s, "%s!%s:%s", a, b, &c);
429     assert(a == "world2" && b == 202 && c == 42.75);
430 }
431 
432 // for backwards compatibility
433 @safe pure unittest
434 {
435     import std.math.operations : isClose;
436     import std.math.traits : isNaN;
437     import std.range.primitives : empty;
438 
439     string s = " 1.2 3.4 ";
440     double x, y, z;
441     assert(formattedRead(s, " %s %s %s ", &x, &y, &z) == 2);
442     assert(s.empty);
443     assert(isClose(x, 1.2));
444     assert(isClose(y, 3.4));
445     assert(isNaN(z));
446 }
447 
448 @safe unittest
449 {
450     string s = "hello!124:34.5";
451     string a;
452     int b;
453     double c;
454     formattedRead(s, "%s!%s:%s", &a, &b, &c);
455     assert(a == "hello" && b == 124 && c == 34.5);
456 }
457 
458 @safe pure unittest
459 {
460     string line;
461 
462     bool f1;
463 
464     line = "true";
465     formattedRead(line, "%s", &f1);
466     assert(f1);
467 
468     line = "TrUE";
469     formattedRead(line, "%s", &f1);
470     assert(f1);
471 
472     line = "false";
473     formattedRead(line, "%s", &f1);
474     assert(!f1);
475 
476     line = "fALsE";
477     formattedRead(line, "%s", &f1);
478     assert(!f1);
479 
480     line = "1";
481     formattedRead(line, "%d", &f1);
482     assert(f1);
483 
484     line = "-1";
485     formattedRead(line, "%d", &f1);
486     assert(f1);
487 
488     line = "0";
489     formattedRead(line, "%d", &f1);
490     assert(!f1);
491 
492     line = "-0";
493     formattedRead(line, "%d", &f1);
494     assert(!f1);
495 }
496 
497 @safe pure unittest
498 {
499     union B
500     {
501         char[int.sizeof] untyped;
502         int typed;
503     }
504 
505     B b;
506     b.typed = 5;
507     char[] input = b.untyped[];
508     int witness;
509     formattedRead(input, "%r", &witness);
510     assert(witness == b.typed);
511 }
512 
513 @safe pure unittest
514 {
515     union A
516     {
517         char[float.sizeof] untyped;
518         float typed;
519     }
520 
521     A a;
522     a.typed = 5.5;
523     char[] input = a.untyped[];
524     float witness;
525     formattedRead(input, "%r", &witness);
526     assert(witness == a.typed);
527 }
528 
529 @safe pure unittest
530 {
531     import std.typecons : Tuple;
532 
533     char[] line = "1 2".dup;
534     int a, b;
535     formattedRead(line, "%s %s", &a, &b);
536     assert(a == 1 && b == 2);
537 
538     line = "10 2 3".dup;
539     formattedRead(line, "%d ", &a);
540     assert(a == 10);
541     assert(line == "2 3");
542 
543     Tuple!(int, float) t;
544     line = "1 2.125".dup;
545     formattedRead(line, "%d %g", &t);
546     assert(t[0] == 1 && t[1] == 2.125);
547 
548     line = "1 7643 2.125".dup;
549     formattedRead(line, "%s %*u %s", &t);
550     assert(t[0] == 1 && t[1] == 2.125);
551 }
552 
553 @safe pure unittest
554 {
555     string line;
556 
557     char c1, c2;
558 
559     line = "abc";
560     formattedRead(line, "%s%c", &c1, &c2);
561     assert(c1 == 'a' && c2 == 'b');
562     assert(line == "c");
563 }
564 
565 @safe pure unittest
566 {
567     string line;
568 
569     line = "[1,2,3]";
570     int[] s1;
571     formattedRead(line, "%s", &s1);
572     assert(s1 == [1,2,3]);
573 }
574 
575 @safe pure unittest
576 {
577     string line;
578 
579     line = "[1,2,3]";
580     int[] s1;
581     formattedRead(line, "[%(%s,%)]", &s1);
582     assert(s1 == [1,2,3]);
583 
584     line = `["hello", "world"]`;
585     string[] s2;
586     formattedRead(line, "[%(%s, %)]", &s2);
587     assert(s2 == ["hello", "world"]);
588 
589     line = "123 456";
590     int[] s3;
591     formattedRead(line, "%(%s %)", &s3);
592     assert(s3 == [123, 456]);
593 
594     line = "h,e,l,l,o; w,o,r,l,d";
595     string[] s4;
596     formattedRead(line, "%(%(%c,%); %)", &s4);
597     assert(s4 == ["hello", "world"]);
598 }
599 
600 @safe pure unittest
601 {
602     import std.exception : assertThrown;
603 
604     string line;
605 
606     int[4] sa1;
607     line = `[1,2,3,4]`;
608     formattedRead(line, "%s", &sa1);
609     assert(sa1 == [1,2,3,4]);
610 
611     int[4] sa2;
612     line = `[1,2,3]`;
613     assertThrown(formattedRead(line, "%s", &sa2));
614 
615     int[4] sa3;
616     line = `[1,2,3,4,5]`;
617     assertThrown(formattedRead(line, "%s", &sa3));
618 }
619 
620 @safe pure unittest
621 {
622     import std.exception : assertThrown;
623     import std.format : FormatException;
624 
625     string input;
626 
627     int[4] sa1;
628     input = `[1,2,3,4]`;
629     formattedRead(input, "[%(%s,%)]", &sa1);
630     assert(sa1 == [1,2,3,4]);
631 
632     int[4] sa2;
633     input = `[1,2,3]`;
634     assertThrown!FormatException(formattedRead(input, "[%(%s,%)]", &sa2));
635 }
636 
637 @safe pure unittest
638 {
639     string line;
640 
641     string s1, s2;
642 
643     line = "hello, world";
644     formattedRead(line, "%s", &s1);
645     assert(s1 == "hello, world", s1);
646 
647     line = "hello, world;yah";
648     formattedRead(line, "%s;%s", &s1, &s2);
649     assert(s1 == "hello, world", s1);
650     assert(s2 == "yah", s2);
651 
652     line = `['h','e','l','l','o']`;
653     string s3;
654     formattedRead(line, "[%(%s,%)]", &s3);
655     assert(s3 == "hello");
656 
657     line = `"hello"`;
658     string s4;
659     formattedRead(line, "\"%(%c%)\"", &s4);
660     assert(s4 == "hello");
661 }
662 
663 @safe pure unittest
664 {
665     string line;
666 
667     string[int] aa1;
668     line = `[1:"hello", 2:"world"]`;
669     formattedRead(line, "%s", &aa1);
670     assert(aa1 == [1:"hello", 2:"world"]);
671 
672     int[string] aa2;
673     line = `{"hello"=1; "world"=2}`;
674     formattedRead(line, "{%(%s=%s; %)}", &aa2);
675     assert(aa2 == ["hello":1, "world":2]);
676 
677     int[string] aa3;
678     line = `{[hello=1]; [world=2]}`;
679     formattedRead(line, "{%([%(%c%)=%s]%|; %)}", &aa3);
680     assert(aa3 == ["hello":1, "world":2]);
681 }
682 
683 // test rvalue using
684 @safe pure unittest
685 {
686     string[int] aa1;
687     formattedRead!("%s")(`[1:"hello", 2:"world"]`, aa1);
688     assert(aa1 == [1:"hello", 2:"world"]);
689 
690     int[string] aa2;
691     formattedRead(`{"hello"=1; "world"=2}`, "{%(%s=%s; %)}", aa2);
692     assert(aa2 == ["hello":1, "world":2]);
693 }
694 
695 /**
696 Reads a value from the given _input range and converts it according to a
697 format specifier.
698 
699 Params:
700     input = the $(REF_ALTTEXT input range, isInputRange, std, range, primitives),
701             to read from
702     spec = a $(MREF_ALTTEXT format string, std,format)
703     T = type to return
704     Range = the type of the input range `input`
705     Char = the character type used for `spec`
706 
707 Returns:
708     A value from `input` of type `T`.
709 
710 Throws:
711     A $(REF_ALTTEXT FormatException, FormatException, std, format)
712     if reading did not succeed.
713 
714 See_Also:
715     $(REF parse, std, conv) and $(REF to, std, conv)
716  */
717 T unformatValue(T, Range, Char)(ref Range input, scope const ref FormatSpec!Char spec)
718 {
719     return unformatValueImpl!T(input, spec);
720 }
721 
722 ///
723 @safe pure unittest
724 {
725     import std.format.spec : singleSpec;
726 
727     string s = "42";
728     auto spec = singleSpec("%s");
729     assert(unformatValue!int(s, spec) == 42);
730 }
731 
732 // https://issues.dlang.org/show_bug.cgi?id=7241
733 @safe pure unittest
734 {
735     string input = "a";
736     auto spec = FormatSpec!char("%s");
737     spec.readUpToNextSpec(input);
738     auto result = unformatValue!(dchar[1])(input, spec);
739     assert(result[0] == 'a');
740 }
741 
742 // https://issues.dlang.org/show_bug.cgi?id=20393
743 @safe pure unittest
744 {
745     import std.exception : assertThrown;
746     string str = "foo 12a-buzz";
747     string a, c;
748     int b;
749     assertThrown(formattedRead(str, "%s %d-%s", &a, &b, &c));
750 }
751 
752 // https://issues.dlang.org/show_bug.cgi?id=18051
753 @safe pure unittest
754 {
755     import std.format : format;
756 
757     enum Op { lt, gt, eq }
758 
759     auto s = format!"%s"(Op.lt);
760     Op op;
761     assert(formattedRead!"%s"(s, op) == 1);
762     assert(op == Op.lt);
763 }