Thread overview
std.range pipelike interface, inverting OutputStreams?
Aug 13, 2016
cy
Aug 13, 2016
cy
Aug 13, 2016
ikod
August 13, 2016
I was trying to use std.regex, and it takes an output stream to pump the result to, which is great, but I wanted to perform TWO replacements, one with the output of the previous, with data that might be a tricky size to cache redundantly.

So, ideally when you do something like regexp replacing, you'd have a function like:
InputRange replaceThingy(InputRange source, someregex, somereplacer);

It doesn't do that, unfortunately, so I can't easily chain replacements. But couldn't I do a thing like...

InputRange pipe(void delegate(ref OutputRange)) { ? }

and then just...
  auto result = pipe((sink) => replaceAllInto(sink, data, firstmatch, firstreplacer));
  auto result2 = pipe((sink) => replaceAllInto(sink, result, secondmatch, secondreplacer));
  copy(result2,stdout);

... or something like that? Has that been done before? Can it be done?

I'm pretty sure any algorithm that takes an output stream can be transformed into an algorithm that provides an input stream, but I'm not sure if I'd be reinventing the wheel here, or if my assumptions are seriously off.

Hoping to avoid weird stuff like context switches... eh.
August 13, 2016
Here's how to do it using context switches. There ought to be a way to manually pass specific state around to keep that from happening, but probably not since there's no interface to pause something writing to an OutputRange.

auto pipe(T, alias oh)()
{
	import std.concurrency: Generator, yield;
	return new Generator!T({
			struct OutputRange {
				void put(T item) {
					yield(item);
				}
			}
			oh(OutputRange());
		});
}

August 13, 2016
On Saturday, 13 August 2016 at 02:15:55 UTC, cy wrote:
> I was trying to use std.regex, and it takes an output stream to pump the result to, which is great, but I wanted to perform TWO replacements, one with the output of the previous, with data that might be a tricky size to cache redundantly.
>
> So, ideally when you do something like regexp replacing, you'd have a function like:
> InputRange replaceThingy(InputRange source, someregex, somereplacer);
>
> It doesn't do that, unfortunately, so I can't easily chain replacements. But couldn't I do a thing like...
>
> InputRange pipe(void delegate(ref OutputRange)) { ? }
>
> and then just...
>   auto result = pipe((sink) => replaceAllInto(sink, data, firstmatch, firstreplacer));
>   auto result2 = pipe((sink) => replaceAllInto(sink, result, secondmatch, secondreplacer));
>   copy(result2,stdout);
>
> ... or something like that? Has that been done before? Can it be done?
>
> I'm pretty sure any algorithm that takes an output stream can be transformed into an algorithm that provides an input stream, but I'm not sure if I'd be reinventing the wheel here, or if my assumptions are seriously off.
>
> Hoping to avoid weird stuff like context switches... eh.

Looks like you can't just because std.regex replace* functions accept only someString as input.

But you can write something like this (sorry for dirty code):
import std.regex;
import std.stdio;

class ReplacePipe {
    private {
        Regex!char   _re;
        string       _format;
        string       _input;
        ReplacePipe  _next;
    }
    this(string input, string r, string f) {
        _re = regex(r);
        _format = f;
        _input = input;
    }
    this(ReplacePipe next, string r, string f) {
        _re = regex(r);
        _format = f;
        _next = next;
    }
    string go(string i) {
        return replaceAll(i, _re, _format);
    }
    string go() {
        if ( _next ) {
            auto s = _next.go();
            return replaceAll(s, _re, _format);;
        }
        return go(_input);
    }
}
ReplacePipe replacePipe(string data, string r, string c) {
    return new ReplacePipe(data, r, c);
}
ReplacePipe replacePipe(ReplacePipe output, string r, string c) {
    return new ReplacePipe(output, r, c);
}
void main() {
    string input = "1 2, 3 4";
    auto a = input.replacePipe(r" ", "+").replacePipe(r"\+", ".").go();
    assert(a=="1.2,.3.4");
}