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: include:
- name: D-Scanner - name: D-Scanner
d: dmd-$LATEST d: dmd-$LATEST
env: DSCANNER=0.5.11 env: DSCANNER=0.6.0
os: linux os: linux
- name: DDoc - name: DDoc
d: dmd-$LATEST d: dmd-$LATEST
@ -32,7 +32,7 @@ matrix:
allow_failures: allow_failures:
- name: D-Scanner - name: D-Scanner
d: dmd-$LATEST d: dmd-$LATEST
env: DSCANNER=0.5.11 env: DSCANNER=0.6.0
os: linux os: linux
addons: addons:

View File

@ -30,7 +30,7 @@
* *
* More advanced formatting is currently not implemented. * 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/, * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
* Mozilla Public License, v. 2.0). * Mozilla Public License, v. 2.0).
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner) * Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
@ -47,8 +47,7 @@ static import tanya.memory.op;
import tanya.meta.metafunction; import tanya.meta.metafunction;
import tanya.meta.trait; import tanya.meta.trait;
import tanya.meta.transform; import tanya.meta.transform;
import tanya.range.array; import tanya.range;
import tanya.range.primitive;
import tanya.typecons : Tuple; import tanya.typecons : Tuple;
// Returns the last part of buffer with converted number. // 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) if (isFloatingPoint!T)
{ {
char[512] buffer; // Big enough for e+308 or e-307. char[512] buffer; // Big enough for e+308 or e-307.
@ -2017,11 +2016,11 @@ if (isFloatingPoint!T)
if (negative) if (negative)
{ {
result.insertBack('-'); put(result, "-");
} }
if (decimalPoint == special) if (decimalPoint == special)
{ {
result.insertBack(realString); put(result, realString);
return; return;
} }
@ -2181,11 +2180,11 @@ if (isFloatingPoint!T)
// Get the length that we've copied. // Get the length that we've copied.
length = cast(uint) (buffer.length - bufferSlice.length); length = cast(uint) (buffer.length - bufferSlice.length);
result.insertBack(buffer[64 .. length]); // Number. put(result, buffer[64 .. length]); // Number.
result.insertBack(tail[1 .. tail[0] + 1]); // Tail. 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)) if (is(T == struct))
{ {
template pred(alias f) template pred(alias f)
@ -2202,24 +2201,24 @@ if (is(T == struct))
} }
alias fields = Filter!(pred, __traits(allMembers, T)); alias fields = Filter!(pred, __traits(allMembers, T));
result.insertBack(T.stringof); put(result, T.stringof);
result.insertBack('('); put(result, "(");
static if (fields.length > 0) static if (fields.length > 0)
{ {
printToString!"{}"(result, __traits(getMember, arg, fields[0])); printToString!"{}"(result, __traits(getMember, arg, fields[0]));
foreach (field; fields[1 .. $]) foreach (field; fields[1 .. $])
{ {
result.insertBack(", "); put(result, ", ");
printToString!"{}"(result, __traits(getMember, arg, field)); 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) if (isInputRange!T && !isInfinite!T)
{ {
result.insertBack('['); put(result, "[");
if (!arg.empty) if (!arg.empty)
{ {
printToString!"{}"(result, arg.front); printToString!"{}"(result, arg.front);
@ -2227,24 +2226,24 @@ if (isInputRange!T && !isInfinite!T)
} }
foreach (e; arg) foreach (e; arg)
{ {
result.insertBack(", "); put(result, ", ");
printToString!"{}"(result, e); 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) auto ref Args args)
{ {
alias Arg = Args[0]; alias Arg = Args[0];
static if (is(Unqual!Arg == typeof(null))) // null static if (is(Unqual!Arg == typeof(null))) // null
{ {
result.insertBack("null"); put(result, "null");
} }
else static if (is(Unqual!Arg == bool)) // Boolean 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 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)) if (args[0] == __traits(getMember, Arg, m))
{ {
result.insertBack(m); put(result, m);
} }
} }
} }
else static if (isSomeChar!Arg || isSomeString!Arg) // String or char else static if (isSomeChar!Arg || isSomeString!Arg) // String or char
{ {
result.insertBack(args[0]); put(result, args[0]);
} }
else static if (isInputRange!Arg else static if (isInputRange!Arg
&& !isInfinite!Arg && !isInfinite!Arg
&& isSomeChar!(ElementType!Arg)) // Stringish range && isSomeChar!(ElementType!Arg)) // Stringish range
{ {
result.insertBack(args[0]); put(result, args[0]);
} }
else static if (isInputRange!Arg && !isInfinite!Arg) 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) if (args[0] is null)
{ {
result.insertBack("null"); put(result, "null");
} }
else else
{ {
result.insertBack(args[0].stringify()[]); put(result, args[0].stringify()[]);
} }
} }
else else
{ {
result.insertBack(args[0].stringify()[]); put(result, args[0].stringify()[]);
} }
} }
else static if (is(Arg == class)) 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)) else static if (is(Arg == interface))
{ {
result.insertBack(Arg.classinfo.name); put(result, Arg.classinfo.name);
} }
else static if (is(Arg == struct)) 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)) else static if (is(Arg == union))
{ {
result.insertBack(Arg.stringof); put(result, Arg.stringof);
} }
else static if (isFloatingPoint!Arg) // Float else static if (isFloatingPoint!Arg) // Float
{ {
@ -2321,21 +2320,19 @@ private ref String printToString(string fmt, Args...)(return ref String result,
} }
while (address != 0); while (address != 0);
result.insertBack("0x"); put(result, "0x");
result.insertBack(buffer[position .. $]); put(result, buffer[position .. $]);
} }
else static if (isIntegral!Arg) // Integer else static if (isIntegral!Arg) // Integer
{ {
char[21] buffer; char[21] buffer;
result.insertBack(integral2String(args[0], buffer)); put(result, integral2String(args[0], buffer));
} }
else else
{ {
static assert(false, static assert(false,
"Formatting type " ~ Arg.stringof ~ " is not supported"); "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 format(string fmt, Args...)(auto ref Args args)
{ {
String formatted; 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; alias Specs = ParseFmt!fmt;
enum bool FormatSpecFilter(alias spec) = is(typeof(spec) == FormatSpec); enum bool FormatSpecFilter(alias spec) = is(typeof(spec) == FormatSpec);
static assert((Filter!(FormatSpecFilter, ParseFmt!fmt)).length == Args.length, 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) foreach (spec; Specs)
{ {
static if (FormatSpecFilter!spec) static if (FormatSpecFilter!spec)
{ {
printToString!"{}"(formatted, args[spec.position]); printToString!"{}"(output, args[spec.position]);
} }
else static if (isSomeString!(typeof(spec))) else static if (isSomeString!(typeof(spec)))
{ {
formatted.insertBack(spec); put(output, spec);
} }
else else
{ {
static assert(false, "Format string parsed incorrectly"); static assert(false, "Format string parsed incorrectly");
} }
} }
return formatted; return output;
} }
// doesn't print the first argument repeatedly // 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/. */ * 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/, * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
* Mozilla Public License, v. 2.0). * Mozilla Public License, v. 2.0).
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner) * Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
@ -13,3 +13,53 @@
* tanya/range/adapter.d) * tanya/range/adapter.d)
*/ */
module tanya.range.adapter; 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));
}