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 }