Comments (6)
This kind of problem is part of the reason for 2.0 so hopefully this should work for you.
You don't actually need to use the Positioner
trait as that is only there to work with the State
type which itself is a remnant from 1.0 (not saying it's not useful though). With 2.0 however the concept of positions were moved into the Stream
trait itself, disconnecting it from the values of the stream. So what you can do is implement a newtype over &[Value]
which works just like the normal &[Value]
stream only you specialise position
to do what you want. It does require a newtype but that wrapping is at least only needed once in the entry point. You can look at SliceStream
for an example.
from combine.
OK, this makes sense and I see it now. In general, marking in some way (deprecated?) 1.0 things would help, since this distinction is not clear.
I think a generic State
is valuable in many cases. Would you accept a patch to separate the Positioner
from the Item
in State
, or a patch add a new State
-like thing that accepts a positioner to do this work?
from combine.
It's not hard to wire this up; others may find this useful if some variant of this doesn't land upstream:
/// Trait for updating the position for types which can be yielded from a `Stream`.
pub trait Positioner: PartialEq {
type Item;
/// The type which keeps track of the position
type Position: Clone + Ord;
/// Creates a start position
fn start() -> Self::Position;
/// Updates the position given that `self` has been taken from the stream
fn update(&self, item: &Self::Item, position: &mut Self::Position);
}
/// The `State2<I>` struct keeps track of the current position in the stream `I` using the
/// `Positioner` trait to update the position.
#[derive(Clone, PartialEq)]
pub struct State2<I, X, P>
where I: Stream<Position=P>,
X: Positioner<Position=P>,
P: Clone + Ord
{
/// The input stream used when items are requested
pub input: I,
/// The positioner used to update the current position
pub positioner: X,
/// The current position
pub position: P,
}
impl<I, X, P> fmt::Debug for State2<I, X, P>
where I: Stream<Position=P> + fmt::Debug,
X: Positioner<Position=P>,
P: Clone + Ord + fmt::Debug
{
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f,
"State2 {{ position: {:?}, input: {:?} }}",
self.position,
self.input)
}
}
impl<I, X, P> State2<I, X, P>
where I: Stream<Position=P>,
X: Positioner<Position=P>,
P: Clone + Ord
{
/// Creates a new `State2<I, X, P>` from an input stream and a positioner. Initializes the position to
/// `X::start()`
pub fn new(input: I, positioner: X) -> State2<I, X, P> {
State2::new_with_position(input, positioner, X::start())
}
/// Creates a new `State2<I, X, P>` from an input stream and a positioner, and sets the initial position.
pub fn new_with_position(input: I, positioner: X, position: P) -> State2<I, X, P> {
State2 {
input: input,
positioner: positioner,
position: position,
}
}
}
impl<I, X, P> combine::StreamOnce for State2<I, X, P>
where I: Stream<Position=P>,
X: Positioner<Item=I::Item, Position=P>,
P: Clone + Ord
{
type Item = I::Item;
type Range = I::Range;
type Position = P;
#[inline]
fn uncons(&mut self) -> Result<I::Item, combine::primitives::Error<I::Item, I::Range>> {
match self.input.uncons() {
Ok(c) => {
self.positioner.update(&c, &mut self.position);
Ok(c)
}
Err(err) => Err(err),
}
}
#[inline(always)]
fn position(&self) -> Self::Position {
self.position.clone()
}
}
#[derive(Clone, PartialEq)]
struct ValuePositioner;
impl Positioner for ValuePositioner {
type Item = Value;
type Position = usize;
fn start() -> usize {
0
}
fn update(&self, item: &Value, position: &mut usize) {
match *item {
Value::Integer(ref x) => *position += (*x as usize),
_ => *position += 1
}
}
}
#[test]
fn test_() {
let input = [Value::Integer(2), Value::Integer(3)];
let mut parser = many(any());
let result = parser.parse(State2::new(&input[..], ValuePositioner));
assert_eq!(result,
Ok((vec![Value::Integer(2)], State2::new_with_position(&[Value::Integer(3)][..], ValuePositioner, 1))));
}
from combine.
Ah, its not deprecated perse. For instance, State<&str>
is really useful since it tracks line + column positions. Just meant that it is not the most general way of achieving what you want (though if it does fit ones usecase then it can be a bit simpler).
And something like your example was what I was going to suggest, probably the important thing is the disconnect between Item : Positioner
. Copying from your example, would it work with this abstraction as well:
/// Trait for updating the position for types which can be yielded from a `Stream`.
pub trait Positioner: PartialEq {
type Item;
/// The type which keeps track of the position
type Position: Clone + Ord;
/// Returns the current position
fn position(&self) -> Self::Position;
/// Updates the position given that `self` has been taken from the stream
fn update(&mut self, item: &Self::Item);
}
/// The `State2<I>` struct keeps track of the current position in the stream `I` using the
/// `Positioner` trait to update the position.
#[derive(Clone, PartialEq)]
pub struct State2<I, X>
where I: Stream,
X: Positioner<Position=I::Position>
{
/// The input stream used when items are requested
pub input: I,
/// The positioner used to update the current position
pub positioner: X,
}
impl<I, X, P> State2<I, X, P>
where I: Stream<Position=P>,
X: Positioner<Position=P>,
P: Clone + Ord
{
/// Creates a new `State2<I, X, P>` from an input stream and a positioner. Initializes the position to
/// `X::start()`
pub fn new(input: I, positioner: X) -> State2<I, X, P> {
State2 {
input: input,
positioner: positioner,
}
}
}
impl<I, X, P> combine::StreamOnce for State2<I, X, P>
where I: Stream<Position=P>,
X: Positioner<Item=I::Item, Position=P>,
P: Clone + Ord
{
type Item = I::Item;
type Range = I::Range;
type Position = P;
#[inline]
fn uncons(&mut self) -> Result<I::Item, combine::primitives::Error<I::Item, I::Range>> {
match self.input.uncons() {
Ok(c) => {
self.positioner.update(&c);
Ok(c)
}
Err(err) => Err(err),
}
}
#[inline(always)]
fn position(&self) -> Self::Position {
self.positioner.position()
}
}
#[derive(Clone, PartialEq)]
struct ValuePositioner(usize);
impl Positioner for ValuePositioner {
type Item = Value;
type Position = usize;
fn start() -> usize {
self.0
}
fn update(&mut self, item: &Value) {
match *item {
Value::Integer(ref x) => self.0 += (*x as usize),
_ => self.0 += 1
}
}
}
Possibly it could be simplified further by making the position itself a Positioner
, would remove fn position
as well.
from combine.
I think a generic State is valuable in many cases. Would you accept a patch to separate the Positioner from the Item in State, or a patch add a new State-like thing that accepts a positioner to do this work?
I'd be up for including something like this (and later deprecating the current Positioner
trait). Just need to nail down the exact interface.
from combine.
@Marwes your approach, which moves position
into Positioner
, is better than mine. I have WIP that does what I want based on your snippet which I will try to post for review tomorrow.
I did have to add a separate Range
type to Positioner
to support RangeStream
. (This was because I couldn't figure out how to iterate an uncons'ed range while placating the borrow checker.) It might be better to parameterize Positioner
by Input
, which encapsulates Item
and Range
. What do you think?
from combine.
Related Issues (20)
- Throw stream errors HOT 5
- DateTime parser HOT 1
- take_until_bytes() and partial parsing HOT 2
- Is there a way to get `Stream<Token=char>` from `io::Read`? HOT 1
- Tools for debugging recursion problems? HOT 4
- Some issue with error reporting
- Errors include unprintable or awkwardly printed characters. HOT 6
- `expected` error strings always quote what was expected, even if it isn't a literal HOT 3
- How about offset into some data? HOT 3
- Outdated tutorial HOT 1
- Native/abstracted sub-parsers HOT 6
- XML parsing for React.js to Solid.js conversion HOT 4
- Comparison with LALRPOP
- Unbounded mutual recursion in Parser impl HOT 3
- Adivce on reducing code size in WASM target HOT 7
- Docs unclear whether `parser!` should be used on nightly rust HOT 2
- Parse `std::process::Child` stdout
- Successful parser will not clear the error stack HOT 1
- build failure
- Implement Pratt parsing or precedence climbing HOT 4
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from combine.