m4rw3r/chomp

ForEach macro/method/function to make it easier to use Stream

m4rw3r opened this issue · 0 comments

Problem

Cannot use a normal for loop with Source or Stream, the Iterator interface is not compatible with either.

Preferably there should be a for_each method on the Stream trait:

pub trait Stream<'a, 'i> {
    type Item: 'i;

    fn for_each<P, F, T, E>(&'a mut self, mut parser: P, mut body: F)
      where P: FnMut(Input<'i, Self::Item>) -> ParseResult<'i, Self::Item, T, E>,
            F: FnMut(T),
            T: 'i,
            E: 'i;
}

NOTE: Does not work as written above.

Design considerations

The current stream implementation necessitates a mutable borrow for each iteration, since Source might need to fill/reallocate/move data in the internal buffer as well as modify the state of the reader. This makes each return value from Stream::parse prevent any more attempts to parse data until after the first return value has gone out of scope.

Copy types or cloning circumvents this, but then we are not doing zero-copy parsing, and the signature cannot be very general either.

let a = iterator.next();
let b = iterator.next();
println!("a: {:?}, b: {:?}", a, b);

vs:

{
    let a = stream.parse(my_parser);

    print!("a: {:?},", a);
}
{
    let b = stream.parse(my_parser);

    println!("b: {:?},", b);
}

Function calls are also an issue, any call passing stream-data to a generic closure will borrow the return from Stream::parse until the variable storing the function goes out of scope which also borrows the stream itself. This prevents any attempt to write a function or method managing this.

Macro

Writing it as a macro has a few merits, it will avoid most of the issues with generics as all types are known.

The issue with macros is that sometimes some function-calls still cause issues with borrowck, and the panic! macro is among them. So no exit of the loop using panic! with a dump of the error (ParseError carries a reference to the input buffer and panic! seems to be borrowing that for the rest of the function, making it impossible to loop.

The macro also needs to be aware of the autofill feature of any possible Stream, if it needs to invoke a method causing it to prepare more data for the parser (of course the user has to want it to automatically fill itself while looping).