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