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