diff --git a/.travis.yml b/.travis.yml index 60125aa..a358fb0 100644 --- a/.travis.yml +++ b/.travis.yml @@ -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: diff --git a/source/tanya/format.d b/source/tanya/format.d index fa46945..eade00a 100644 --- a/source/tanya/format.d +++ b/source/tanya/format.d @@ -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 diff --git a/source/tanya/range/adapter.d b/source/tanya/range/adapter.d index ec2e5d9..7c3cb5e 100644 --- a/source/tanya/range/adapter.d +++ b/source/tanya/range/adapter.d @@ -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)); +}