Add sformat() writing to an output range
This commit is contained in:
parent
1d3d750adb
commit
0bef2ef76d
@ -23,7 +23,7 @@ matrix:
|
||||
include:
|
||||
- name: D-Scanner
|
||||
d: dmd-$LATEST
|
||||
env: DSCANNER=0.5.11
|
||||
env: DSCANNER=0.6.0
|
||||
os: linux
|
||||
- name: DDoc
|
||||
d: dmd-$LATEST
|
||||
@ -32,7 +32,7 @@ matrix:
|
||||
allow_failures:
|
||||
- name: D-Scanner
|
||||
d: dmd-$LATEST
|
||||
env: DSCANNER=0.5.11
|
||||
env: DSCANNER=0.6.0
|
||||
os: linux
|
||||
|
||||
addons:
|
||||
|
@ -30,7 +30,7 @@
|
||||
*
|
||||
* More advanced formatting is currently not implemented.
|
||||
*
|
||||
* Copyright: Eugene Wissner 2017-2018.
|
||||
* Copyright: Eugene Wissner 2017-2019.
|
||||
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
|
||||
* Mozilla Public License, v. 2.0).
|
||||
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
|
||||
@ -47,8 +47,7 @@ static import tanya.memory.op;
|
||||
import tanya.meta.metafunction;
|
||||
import tanya.meta.trait;
|
||||
import tanya.meta.transform;
|
||||
import tanya.range.array;
|
||||
import tanya.range.primitive;
|
||||
import tanya.range;
|
||||
import tanya.typecons : Tuple;
|
||||
|
||||
// Returns the last part of buffer with converted number.
|
||||
@ -1989,7 +1988,7 @@ private const(char)[] real2String(double value,
|
||||
}
|
||||
}
|
||||
|
||||
private void formatReal(T)(ref T arg, ref String result)
|
||||
private void formatReal(T, OR)(ref T arg, OR result)
|
||||
if (isFloatingPoint!T)
|
||||
{
|
||||
char[512] buffer; // Big enough for e+308 or e-307.
|
||||
@ -2017,11 +2016,11 @@ if (isFloatingPoint!T)
|
||||
|
||||
if (negative)
|
||||
{
|
||||
result.insertBack('-');
|
||||
put(result, "-");
|
||||
}
|
||||
if (decimalPoint == special)
|
||||
{
|
||||
result.insertBack(realString);
|
||||
put(result, realString);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -2181,11 +2180,11 @@ if (isFloatingPoint!T)
|
||||
// Get the length that we've copied.
|
||||
length = cast(uint) (buffer.length - bufferSlice.length);
|
||||
|
||||
result.insertBack(buffer[64 .. length]); // Number.
|
||||
result.insertBack(tail[1 .. tail[0] + 1]); // Tail.
|
||||
put(result, buffer[64 .. length]); // Number.
|
||||
put(result, tail[1 .. tail[0] + 1]); // Tail.
|
||||
}
|
||||
|
||||
private void formatStruct(T)(ref T arg, ref String result)
|
||||
private void formatStruct(T, OR)(ref T arg, OR result)
|
||||
if (is(T == struct))
|
||||
{
|
||||
template pred(alias f)
|
||||
@ -2202,24 +2201,24 @@ if (is(T == struct))
|
||||
}
|
||||
alias fields = Filter!(pred, __traits(allMembers, T));
|
||||
|
||||
result.insertBack(T.stringof);
|
||||
result.insertBack('(');
|
||||
put(result, T.stringof);
|
||||
put(result, "(");
|
||||
static if (fields.length > 0)
|
||||
{
|
||||
printToString!"{}"(result, __traits(getMember, arg, fields[0]));
|
||||
foreach (field; fields[1 .. $])
|
||||
{
|
||||
result.insertBack(", ");
|
||||
put(result, ", ");
|
||||
printToString!"{}"(result, __traits(getMember, arg, field));
|
||||
}
|
||||
}
|
||||
result.insertBack(')');
|
||||
put(result, ")");
|
||||
}
|
||||
|
||||
private void formatRange(T)(ref T arg, ref String result)
|
||||
private void formatRange(T, OR)(ref T arg, OR result)
|
||||
if (isInputRange!T && !isInfinite!T)
|
||||
{
|
||||
result.insertBack('[');
|
||||
put(result, "[");
|
||||
if (!arg.empty)
|
||||
{
|
||||
printToString!"{}"(result, arg.front);
|
||||
@ -2227,24 +2226,24 @@ if (isInputRange!T && !isInfinite!T)
|
||||
}
|
||||
foreach (e; arg)
|
||||
{
|
||||
result.insertBack(", ");
|
||||
put(result, ", ");
|
||||
printToString!"{}"(result, e);
|
||||
}
|
||||
result.insertBack(']');
|
||||
put(result, "]");
|
||||
}
|
||||
|
||||
private ref String printToString(string fmt, Args...)(return ref String result,
|
||||
private void printToString(string fmt, OR, Args...)(ref OR result,
|
||||
auto ref Args args)
|
||||
{
|
||||
alias Arg = Args[0];
|
||||
|
||||
static if (is(Unqual!Arg == typeof(null))) // null
|
||||
{
|
||||
result.insertBack("null");
|
||||
put(result, "null");
|
||||
}
|
||||
else static if (is(Unqual!Arg == bool)) // Boolean
|
||||
{
|
||||
result.insertBack(args[0] ? "true" : "false");
|
||||
put(result, args[0] ? "true" : "false");
|
||||
}
|
||||
else static if (is(Arg == enum)) // Enum
|
||||
{
|
||||
@ -2252,19 +2251,19 @@ private ref String printToString(string fmt, Args...)(return ref String result,
|
||||
{
|
||||
if (args[0] == __traits(getMember, Arg, m))
|
||||
{
|
||||
result.insertBack(m);
|
||||
put(result, m);
|
||||
}
|
||||
}
|
||||
}
|
||||
else static if (isSomeChar!Arg || isSomeString!Arg) // String or char
|
||||
{
|
||||
result.insertBack(args[0]);
|
||||
put(result, args[0]);
|
||||
}
|
||||
else static if (isInputRange!Arg
|
||||
&& !isInfinite!Arg
|
||||
&& isSomeChar!(ElementType!Arg)) // Stringish range
|
||||
{
|
||||
result.insertBack(args[0]);
|
||||
put(result, args[0]);
|
||||
}
|
||||
else static if (isInputRange!Arg && !isInfinite!Arg)
|
||||
{
|
||||
@ -2276,25 +2275,25 @@ private ref String printToString(string fmt, Args...)(return ref String result,
|
||||
{
|
||||
if (args[0] is null)
|
||||
{
|
||||
result.insertBack("null");
|
||||
put(result, "null");
|
||||
}
|
||||
else
|
||||
{
|
||||
result.insertBack(args[0].stringify()[]);
|
||||
put(result, args[0].stringify()[]);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
result.insertBack(args[0].stringify()[]);
|
||||
put(result, args[0].stringify()[]);
|
||||
}
|
||||
}
|
||||
else static if (is(Arg == class))
|
||||
{
|
||||
result.insertBack(args[0] is null ? "null" : args[0].toString());
|
||||
put(result, args[0] is null ? "null" : args[0].toString());
|
||||
}
|
||||
else static if (is(Arg == interface))
|
||||
{
|
||||
result.insertBack(Arg.classinfo.name);
|
||||
put(result, Arg.classinfo.name);
|
||||
}
|
||||
else static if (is(Arg == struct))
|
||||
{
|
||||
@ -2302,7 +2301,7 @@ private ref String printToString(string fmt, Args...)(return ref String result,
|
||||
}
|
||||
else static if (is(Arg == union))
|
||||
{
|
||||
result.insertBack(Arg.stringof);
|
||||
put(result, Arg.stringof);
|
||||
}
|
||||
else static if (isFloatingPoint!Arg) // Float
|
||||
{
|
||||
@ -2321,21 +2320,19 @@ private ref String printToString(string fmt, Args...)(return ref String result,
|
||||
}
|
||||
while (address != 0);
|
||||
|
||||
result.insertBack("0x");
|
||||
result.insertBack(buffer[position .. $]);
|
||||
put(result, "0x");
|
||||
put(result, buffer[position .. $]);
|
||||
}
|
||||
else static if (isIntegral!Arg) // Integer
|
||||
{
|
||||
char[21] buffer;
|
||||
result.insertBack(integral2String(args[0], buffer));
|
||||
put(result, integral2String(args[0], buffer));
|
||||
}
|
||||
else
|
||||
{
|
||||
static assert(false,
|
||||
"Formatting type " ~ Arg.stringof ~ " is not supported");
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -2351,28 +2348,47 @@ private ref String printToString(string fmt, Args...)(return ref String result,
|
||||
String format(string fmt, Args...)(auto ref Args args)
|
||||
{
|
||||
String formatted;
|
||||
sformat!fmt(backInserter(formatted), args);
|
||||
return formatted;
|
||||
}
|
||||
|
||||
/**
|
||||
* Produces a string according to the specified format and writes it into an
|
||||
* output range. $(D_PSYMBOL sformat) writes the final string in chunks, so the
|
||||
* output range should be in output range for `const(char)[]`.
|
||||
*
|
||||
* Params:
|
||||
* fmt = Format.
|
||||
* R = Output range type.
|
||||
* output = Output range.
|
||||
* args = Arguments.
|
||||
*
|
||||
* Returns: $(D_PARAM output).
|
||||
*/
|
||||
R sformat(string fmt, R, Args...)(return R output, auto ref Args args)
|
||||
if (isOutputRange!(R, const(char)[]))
|
||||
{
|
||||
alias Specs = ParseFmt!fmt;
|
||||
enum bool FormatSpecFilter(alias spec) = is(typeof(spec) == FormatSpec);
|
||||
static assert((Filter!(FormatSpecFilter, ParseFmt!fmt)).length == Args.length,
|
||||
"Number of the arguments doesn't match the format strign");
|
||||
"Number of the arguments doesn't match the format string");
|
||||
|
||||
foreach (spec; Specs)
|
||||
{
|
||||
static if (FormatSpecFilter!spec)
|
||||
{
|
||||
printToString!"{}"(formatted, args[spec.position]);
|
||||
printToString!"{}"(output, args[spec.position]);
|
||||
}
|
||||
else static if (isSomeString!(typeof(spec)))
|
||||
{
|
||||
formatted.insertBack(spec);
|
||||
put(output, spec);
|
||||
}
|
||||
else
|
||||
{
|
||||
static assert(false, "Format string parsed incorrectly");
|
||||
}
|
||||
}
|
||||
return formatted;
|
||||
return output;
|
||||
}
|
||||
|
||||
// doesn't print the first argument repeatedly
|
||||
|
@ -3,9 +3,9 @@
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
/**
|
||||
* Range adapters.
|
||||
* Range adapters transform some data structures into ranges.
|
||||
*
|
||||
* Copyright: Eugene Wissner 2018.
|
||||
* Copyright: Eugene Wissner 2018-2019.
|
||||
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
|
||||
* Mozilla Public License, v. 2.0).
|
||||
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
|
||||
@ -13,3 +13,53 @@
|
||||
* tanya/range/adapter.d)
|
||||
*/
|
||||
module tanya.range.adapter;
|
||||
|
||||
import tanya.functional;
|
||||
import tanya.meta.trait;
|
||||
import tanya.range;
|
||||
|
||||
version (unittest)
|
||||
{
|
||||
static struct Container
|
||||
{
|
||||
void insertBack(const(char)[])
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
package (tanya) auto backInserter(Container)(return ref Container container)
|
||||
if (hasMember!(Container, "insertBack"))
|
||||
{
|
||||
static struct BackInserter
|
||||
{
|
||||
private Container* container;
|
||||
|
||||
this(ref Container container) @trusted
|
||||
{
|
||||
this.container = &container;
|
||||
}
|
||||
|
||||
void opCall(T)(auto ref T data)
|
||||
{
|
||||
this.container.insertBack(forward!data);
|
||||
}
|
||||
}
|
||||
return BackInserter(container);
|
||||
}
|
||||
|
||||
@nogc nothrow pure @safe unittest
|
||||
{
|
||||
auto func()()
|
||||
{
|
||||
Container container;
|
||||
return backInserter(container);
|
||||
}
|
||||
static assert(!is(typeof(func!())));
|
||||
}
|
||||
|
||||
@nogc nothrow pure @safe unittest
|
||||
{
|
||||
Container container;
|
||||
static assert(isOutputRange!(typeof(backInserter(container)), string));
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user