1 /++
2 +/
3 module mir.parse;
4 
5 import mir.primitives;
6 import std.range.primitives: isInputRange;
7 
8 /++
9 Throws: nogc Exception in case of parse error or non-empty remaining input.
10 +/
11 T fromString(T, Range)(scope auto ref Range r)
12 {
13     static immutable excne = new Exception("fromString: remaining input is not empty after parsing " ~ T.stringof);
14     static immutable excfp = new Exception("fromString failed to parse " ~ T.stringof);
15 
16     T value;
17     if (parse!T(r, value))
18     {
19         if (r.empty)
20         {
21             return value;
22         }
23         throw excne;
24     }
25     else
26     {
27         throw excfp;
28     }
29 }
30 
31 ///
32 @safe pure @nogc unittest
33 {
34     assert("123".fromString!int == 123);
35 
36     auto s = "123";
37     assert(s.fromString!int == 123);
38     assert(s == "");
39 
40     s = "123";
41     assert(s[].fromString!int == 123);
42     assert(s == "123");
43 }
44 
45 /++
46 +/
47 bool parse(T : byte, Range)(scope ref Range r, scope ref T value)
48     if (isInputRange!Range && !__traits(isUnsigned, T))
49 {
50     int lvalue;
51     auto ret = parse!(int, Range)(r, lvalue);
52     value = cast(byte) lvalue;
53     return ret && value == lvalue;
54 }
55 /// ditto
56 bool parse(T : short, Range)(scope ref Range r, scope ref T value)
57     if (isInputRange!Range && !__traits(isUnsigned, T))
58 {
59     int lvalue;
60     auto ret = parse!(int, Range)(r, lvalue);
61     value = cast(short) lvalue;
62     return ret && value == lvalue;
63 }
64 
65 /// ditto
66 pragma(inline, false)
67 bool parse(T : int, Range)(scope ref Range r, scope ref T value)
68     if (isInputRange!Range && !__traits(isUnsigned, T))
69 {
70     return parseSignedImpl!(int, Range)(r, value);
71 }
72 
73 /// ditto
74 pragma(inline, false)
75 bool parse(T : long, Range)(scope ref Range r, scope ref T value)
76     if (isInputRange!Range && !__traits(isUnsigned, T))
77 {
78     return parseSignedImpl!(long, Range)(r, value);
79 }
80 
81 /// ditto
82 bool parse(T : ubyte, Range)(scope ref Range r, scope ref T value)
83     if (isInputRange!Range && __traits(isUnsigned, T))
84 {
85     uint lvalue;
86     auto ret = parse!(uint, Range)(r, lvalue);
87     value = cast(ubyte) lvalue;
88     return ret && value == lvalue;
89 }
90 
91 /// ditto
92 bool parse(T : ushort, Range)(scope ref Range r, scope ref T value)
93     if (isInputRange!Range && __traits(isUnsigned, T))
94 {
95     uint lvalue;
96     auto ret = parse!(uint, Range)(r, lvalue);
97     value = cast(ushort) lvalue;
98     return ret && value == lvalue;
99 }
100 
101 /// ditto
102 pragma(inline, false)
103 bool parse(T : uint, Range)(scope ref Range r, scope ref T value)
104     if (isInputRange!Range && __traits(isUnsigned, T))
105 {
106     return parseUnsignedImpl!(uint, Range)(r, value);
107 }
108 
109 /// ditto
110 pragma(inline, false)
111 bool parse(T : ulong, Range)(scope ref Range r, scope ref T value)
112     if (isInputRange!Range && __traits(isUnsigned, T))
113 {
114     return parseUnsignedImpl!(ulong, Range)(r, value);
115 }
116 
117 
118 ///
119 unittest
120 {
121     import std.meta: AliasSeq;
122     foreach (T; AliasSeq!(byte, ubyte, short, ushort, int, uint, long, ulong))
123     {
124         auto str = "123";
125         T val;
126         assert(parse(str, val));
127         assert(val == 123);
128         str = "0";
129         assert(parse(str, val));
130         assert(val == 0);
131         str = "9";
132         assert(parse(str, val));
133         assert(val == 9);
134         str = "";
135         assert(!parse(str, val));
136         assert(val == 0);
137         str = "text";
138         assert(!parse(str, val));
139         assert(val == 0);
140     }
141 }
142 
143 ///
144 unittest
145 {
146     import std.meta: AliasSeq;
147     foreach (T; AliasSeq!(byte, short, int, long))
148     {
149         auto str = "-123";
150         T val;
151         assert(parse(str, val));
152         assert(val == -123);
153         str = "-0";
154         assert(parse(str, val));
155         assert(val == 0);
156         str = "-9text";
157         assert(parse(str, val));
158         assert(val == -9);
159         assert(str == "text");
160         enum m = T.min + 0;
161         str = m.stringof;
162         assert(parse(str, val));
163         assert(val == T.min);
164     }
165 }
166 
167 ///
168 unittest
169 {
170     import std.meta: AliasSeq;
171     foreach (T; AliasSeq!(byte, short, int, long))
172     {
173     }
174 }
175 
176 alias r1 = parseUnsignedImpl!(uint, string);
177 alias r2 = parseUnsignedImpl!(ulong, string);
178 alias r3 = parseSignedImpl!(int, string);
179 alias r4 = parseSignedImpl!(long, string);
180 
181 private bool parseUnsignedImpl(T, Range)(scope ref Range r, scope ref T value)
182     if(__traits(isUnsigned, T))
183 {
184     import core.checkedint: addu, mulu;
185 
186     bool sign;
187 B:
188     if (!r.empty)
189     {
190         auto f = r.front + 0u;
191         if (!sign && f == '+')
192         {
193             r.popFront;
194             sign = true;
195             goto B;
196         }
197         uint c = f - '0';
198         if (c >= 10)
199             goto F;
200         T x = c;
201         for(;;)
202         {
203             r.popFront;
204             if (r.empty)
205                 break;
206             c = r.front - '0';
207             if (c >= 10)
208                 break;
209             bool overflow;
210             T y = mulu(x, cast(uint)10, overflow);
211             if (overflow)
212                 goto R;
213             x = y;
214             T z = addu(x, cast(uint)c, overflow);
215             if (overflow)
216                 goto R;
217             x = z;
218         }
219         value = x;
220         return true;
221     }
222 F:  value = 0;
223 R:  return false;
224 }
225 
226 private bool parseSignedImpl(T, Range)(scope ref Range r, scope ref T value)
227     if(!__traits(isUnsigned, T))
228 {
229     import core.checkedint: negs;
230     import std.traits: Unsigned;
231 
232     bool sign;
233 B:
234     if (!r.empty)
235     {
236         auto f = r.front + 0u;
237         if (!sign && f == '-')
238         {
239             r.popFront;
240             sign = true;
241             goto B;
242         }
243         auto retu = (()@trusted=>parse(r, *cast(Unsigned!T*) &value))();
244         // auto retu = false;
245         if (!retu)
246             goto R;
247         if (!sign)
248         {
249             if (value < 0)
250                 goto R;
251         }
252         else
253         {
254             if (value < 0 && value != T.min)
255                 goto R;
256             value = -value;
257         }
258         return true;
259     }
260 F:  value = 0;
261 R:  return false;
262 }