Add sformat() writing to an output range

This commit is contained in:
Eugen Wissner 2019-02-12 07:37:24 +01:00
parent 1d3d750adb
commit 0bef2ef76d
3 changed files with 109 additions and 43 deletions

View File

@ -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:

View File

@ -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,
auto ref Args args)
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

View File

@ -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));
}