Giter VIP home page Giter VIP logo

Comments (6)

Marwes avatar Marwes commented on June 1, 2024

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.

ncalexan avatar ncalexan commented on June 1, 2024

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.

ncalexan avatar ncalexan commented on June 1, 2024

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.

Marwes avatar Marwes commented on June 1, 2024

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.

Marwes avatar Marwes commented on June 1, 2024

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.

ncalexan avatar ncalexan commented on June 1, 2024

@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)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo 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.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.