-
Notifications
You must be signed in to change notification settings - Fork 19
Description
Problem
Currently the input type only allows for slices, and is special cased for situations where it may not be the whole of the input. I cannot provide any line/row/offset counting either since it is a concrete type and an extension with that functionality would impact all code.
This would provide a way to slot in position-aware wrappers to solve #38 neatly.
Proposed solution
Convert Input<I> into a trait, with ret and err as provided methods, the input-token type would be the associated type Token. All the primitive methods (currently provided by InputClone and InputBuffer) are also present but require an instance of the zero-sized type Guard which cannot be instantiated outside of the primitives module (note the private field). The primitives would be reachable through methods on a Primitives trait which has to be used separately (the blanket implementation for all Input makes it possible to easily use it once it is in scope).
use primitives::Guard;
pub use primitives::Primitives;
pub trait Input: Sized {
type Token;
type Marker;
fn ret<T>(self, t: T) -> ParseResult<Self, T> {
ParseResult(self, t)
}
fn _consume(self, usize, Guard) -> Self;
fn _buffer(&self, Guard) -> &[Self::Token];
fn _is_end(&self, Guard) -> bool;
fn _mark(&self, Guard) -> Self::Marker;
fn _restore(self, Self::Marker, Guard) -> Self;
}
pub mod primitives {
use Input;
pub struct Guard(());
pub trait Primitives: Input {
fn consume(self, n: usize) -> Self {
self._consume(Guard(()), n)
}
fn buffer(&self) -> &[Self::Token] {
self._buffer(Guard(()))
}
fn is_end(&self) -> bool {
self._is_end(Guard(()))
}
fn mark(&self) -> Self::Marker {
self._mark(Guard(()))
}
fn restore(self, m: Self::Marker) -> Self {
self._restore(Guard(()), m)
}
}
impl<I: Input> Primitives for I {}
}The mark method is the replacement for InputClone, it should be used with the restore method to restore the state of the Input to the old one.
Pros
Inputcan be implemented directly for slices, eliminating certain branches from parsers and combinators likemany,take_while,eofand so on.- An
Inputimplementation can be provided for line-counting which could be slotted in to provide line-counting in any existing parsers - The
markandrestoremethods would provide mechanisms allowing types which do not wholly consist of slices to work, though thebuffermethod is probably not the right choice for that, it will need a change to eg. support ropes. - All parsers need to be generic, before we could get away with only concrete types since
Input<u8>is a concrete type.Input<Token=u8>will not be a concrete type.
Cons
-
Parser function signature change, very backwards incompatible:
// old fn my_parser<'a, I>(i: Input<'a, I>, ...) -> ParseResult<'a, I, T, E> // old, lifetime elision: fn my_parser<I>(i: Input<I>, ...) -> ParseResult<I, T, E> // new fn my_parser<I: Input>(i: I, ...) -> ParseResult<I, T, E>
-
The type
I: Inputcan no longer be guaranteed to be linear since the#[must_use]annotation cannot be put on the concrete type.This is probably not an issue in practice since the
Itype is required by value to create aParseResultand theParseResultin turn is ultimately required by the functions which start the parsing.