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 }