mirror of
https://github.com/dlang/phobos.git
synced 2025-04-27 13:40:20 +03:00
391 lines
12 KiB
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);
|
|
}
|