phobos/std/format/internal/read.d

391 lines
12 KiB
D

// Written in the D programming language.
/*
Copyright: Copyright The D Language Foundation 2000-2013.
License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0).
Authors: $(HTTP walterbright.com, Walter Bright), $(HTTP erdani.com,
Andrei Alexandrescu), and Kenji Hara
Source: $(PHOBOSSRC std/format/internal/read.d)
*/
module std.format.internal.read;
import std.range.primitives : ElementEncodingType, ElementType, isInputRange;
import std.traits : isAggregateType, isArray, isAssociativeArray,
isDynamicArray, isFloatingPoint, isIntegral, isSomeChar, isSomeString,
isStaticArray, StringTypeOf;
import std.format.spec : FormatSpec;
package(std.format):
void skipData(Range, Char)(ref Range input, scope const ref FormatSpec!Char spec)
{
import std.ascii : isDigit;
import std.range.primitives : empty, front, popFront;
switch (spec.spec)
{
case 'c': input.popFront(); break;
case 'd':
if (input.front == '+' || input.front == '-') input.popFront();
goto case 'u';
case 'u':
while (!input.empty && isDigit(input.front)) input.popFront();
break;
default:
assert(0, "Format specifier not understood: %" ~ spec.spec);
}
}
private template acceptedSpecs(T)
{
static if (isIntegral!T)
enum acceptedSpecs = "bdosuxX";
else static if (isFloatingPoint!T)
enum acceptedSpecs = "seEfgG";
else static if (isSomeChar!T)
enum acceptedSpecs = "bcdosuxX"; // integral + 'c'
else
enum acceptedSpecs = "";
}
T unformatValueImpl(T, Range, Char)(ref Range input, scope const ref FormatSpec!Char spec)
if (isInputRange!Range && is(immutable T == immutable bool))
{
import std.algorithm.searching : find;
import std.conv : parse, text;
import std.format : enforceFmt, unformatValue;
if (spec.spec == 's') return parse!T(input);
enforceFmt(find(acceptedSpecs!long, spec.spec).length,
text("Wrong unformat specifier '%", spec.spec , "' for ", T.stringof));
return unformatValue!long(input, spec) != 0;
}
T unformatValueImpl(T, Range, Char)(ref Range input, scope const ref FormatSpec!Char spec)
if (isInputRange!Range && is(T == typeof(null)))
{
import std.conv : parse, text;
import std.format : enforceFmt;
enforceFmt(spec.spec == 's',
text("Wrong unformat specifier '%", spec.spec , "' for ", T.stringof));
return parse!T(input);
}
T unformatValueImpl(T, Range, Char)(ref Range input, scope const ref FormatSpec!Char spec)
if (isInputRange!Range && isIntegral!T && !is(T == enum) && isSomeChar!(ElementType!Range))
{
import std.algorithm.searching : find;
import std.conv : parse, text;
import std.format : enforceFmt, FormatException;
if (spec.spec == 'r')
{
static if (is(immutable ElementEncodingType!Range == immutable char)
|| is(immutable ElementEncodingType!Range == immutable byte)
|| is(immutable ElementEncodingType!Range == immutable ubyte))
return rawRead!T(input);
else
throw new FormatException(
"The raw read specifier %r may only be used with narrow strings and ranges of bytes."
);
}
enforceFmt(find(acceptedSpecs!T, spec.spec).length,
text("Wrong unformat specifier '%", spec.spec , "' for ", T.stringof));
enforceFmt(spec.width == 0, "Parsing integers with a width specification is not implemented"); // TODO
immutable uint base =
spec.spec == 'x' || spec.spec == 'X' ? 16 :
spec.spec == 'o' ? 8 :
spec.spec == 'b' ? 2 :
spec.spec == 's' || spec.spec == 'd' || spec.spec == 'u' ? 10 : 0;
assert(base != 0, "base must be not equal to zero");
return parse!T(input, base);
}
T unformatValueImpl(T, Range, Char)(ref Range input, scope const ref FormatSpec!Char spec)
if (isFloatingPoint!T && !is(T == enum) && isInputRange!Range
&& isSomeChar!(ElementType!Range)&& !is(Range == enum))
{
import std.algorithm.searching : find;
import std.conv : parse, text;
import std.format : enforceFmt, FormatException;
if (spec.spec == 'r')
{
static if (is(immutable ElementEncodingType!Range == immutable char)
|| is(immutable ElementEncodingType!Range == immutable byte)
|| is(immutable ElementEncodingType!Range == immutable ubyte))
return rawRead!T(input);
else
throw new FormatException(
"The raw read specifier %r may only be used with narrow strings and ranges of bytes."
);
}
enforceFmt(find(acceptedSpecs!T, spec.spec).length,
text("Wrong unformat specifier '%", spec.spec , "' for ", T.stringof));
return parse!T(input);
}
T unformatValueImpl(T, Range, Char)(ref Range input, scope const ref FormatSpec!Char spec)
if (isInputRange!Range && isSomeChar!T && !is(T == enum) && isSomeChar!(ElementType!Range))
{
import std.algorithm.searching : find;
import std.conv : to, text;
import std.range.primitives : empty, front, popFront;
import std.format : enforceFmt, unformatValue;
if (spec.spec == 's' || spec.spec == 'c')
{
auto result = to!T(input.front);
input.popFront();
return result;
}
enforceFmt(find(acceptedSpecs!T, spec.spec).length,
text("Wrong unformat specifier '%", spec.spec , "' for ", T.stringof));
enum int size = T.sizeof;
static if (size == 1)
return unformatValue!ubyte(input, spec);
else static if (size == 2)
return unformatValue!ushort(input, spec);
else static if (size == 4)
return unformatValue!uint(input, spec);
else
static assert(false, T.stringof ~ ".sizeof must be 1, 2, or 4 not " ~
size.stringof);
}
T unformatValueImpl(T, Range, Char)(ref Range input, scope const ref FormatSpec!Char fmt)
if (isInputRange!Range && is(StringTypeOf!T) && !isAggregateType!T && !is(T == enum))
{
import std.conv : text;
import std.range.primitives : empty, front, popFront, put;
import std.format : enforceFmt;
const spec = fmt.spec;
if (spec == '(')
{
return unformatRange!T(input, fmt);
}
enforceFmt(spec == 's',
text("Wrong unformat specifier '%", spec , "' for ", T.stringof));
static if (isStaticArray!T)
{
T result;
auto app = result[];
}
else
{
import std.array : appender;
auto app = appender!T();
}
if (fmt.trailing.empty)
{
for (; !input.empty; input.popFront())
{
static if (isStaticArray!T)
if (app.empty)
break;
app.put(input.front);
}
}
else
{
immutable end = fmt.trailing.front;
for (; !input.empty && input.front != end; input.popFront())
{
static if (isStaticArray!T)
if (app.empty)
break;
app.put(input.front);
}
}
static if (isStaticArray!T)
{
enforceFmt(app.empty, "need more input");
return result;
}
else
return app.data;
}
T unformatValueImpl(T, Range, Char)(ref Range input, scope const ref FormatSpec!Char fmt)
if (isInputRange!Range && !is(StringTypeOf!T) && !isAggregateType!T
&& (isArray!T || isAssociativeArray!T || is(T == enum)))
{
import std.conv : parse, text;
import std.format : enforceFmt;
const spec = fmt.spec;
if (spec == '(')
{
return unformatRange!T(input, fmt);
}
enforceFmt(spec == 's',
text("Wrong unformat specifier '%", spec , "' for ", T.stringof));
return parse!T(input);
}
/*
* Function that performs raw reading. Used by unformatValue
* for integral and float types.
*/
private T rawRead(T, Range)(ref Range input)
if (is(immutable ElementEncodingType!Range == immutable char)
|| is(immutable ElementEncodingType!Range == immutable byte)
|| is(immutable ElementEncodingType!Range == immutable ubyte))
{
import std.range.primitives : popFront;
union X
{
ubyte[T.sizeof] raw;
T typed;
}
X x;
foreach (i; 0 .. T.sizeof)
{
static if (isSomeString!Range)
{
x.raw[i] = input[0];
input = input[1 .. $];
}
else
{
// TODO: recheck this
x.raw[i] = input.front;
input.popFront();
}
}
return x.typed;
}
private T unformatRange(T, Range, Char)(ref Range input, scope const ref FormatSpec!Char spec)
in (spec.spec == '(', "spec.spec must be '(' not " ~ spec.spec)
{
import std.range.primitives : empty, front, popFront;
import std.format : enforceFmt, format;
T result;
static if (isStaticArray!T)
{
size_t i;
}
const(Char)[] cont = spec.trailing;
for (size_t j = 0; j < spec.trailing.length; ++j)
{
if (spec.trailing[j] == '%')
{
cont = spec.trailing[0 .. j];
break;
}
}
bool checkEnd()
{
return input.empty || !cont.empty && input.front == cont.front;
}
if (!checkEnd())
{
for (;;)
{
auto fmt = FormatSpec!Char(spec.nested);
fmt.readUpToNextSpec(input);
enforceFmt(!input.empty, "Unexpected end of input when parsing range");
static if (isStaticArray!T)
{
result[i++] = unformatElement!(typeof(T.init[0]))(input, fmt);
}
else static if (isDynamicArray!T)
{
import std.conv : WideElementType;
result ~= unformatElement!(WideElementType!T)(input, fmt);
}
else static if (isAssociativeArray!T)
{
auto key = unformatElement!(typeof(T.init.keys[0]))(input, fmt);
fmt.readUpToNextSpec(input); // eat key separator
result[key] = unformatElement!(typeof(T.init.values[0]))(input, fmt);
}
static if (isStaticArray!T)
{
enforceFmt(i <= T.length,
"Too many format specifiers for static array of length %d".format(T.length));
}
if (spec.sep !is null)
fmt.readUpToNextSpec(input);
auto sep = spec.sep !is null ? spec.sep : fmt.trailing;
if (checkEnd())
break;
if (!sep.empty && input.front == sep.front)
{
while (!sep.empty)
{
enforceFmt(!input.empty,
"Unexpected end of input when parsing range separator");
enforceFmt(input.front == sep.front,
"Unexpected character when parsing range separator");
input.popFront();
sep.popFront();
}
}
}
}
static if (isStaticArray!T)
{
enforceFmt(i == T.length,
"Too few (%d) format specifiers for static array of length %d".format(i, T.length));
}
return result;
}
T unformatElement(T, Range, Char)(ref Range input, scope const ref FormatSpec!Char spec)
if (isInputRange!Range)
{
import std.conv : parseElement;
import std.format.read : unformatValue;
static if (isSomeString!T)
{
if (spec.spec == 's')
{
return parseElement!T(input);
}
}
else static if (isSomeChar!T)
{
if (spec.spec == 's')
{
return parseElement!T(input);
}
}
return unformatValue!T(input, spec);
}