Search code examples
f#system.reactiveparser-combinatorsfparsec

FParsec Reactive Example


I'm hopeful that someone could potentially post an example of using FParsec where the data is based on some sort of incoming live stream.

Some examples could be producing a result based on mouse gestures, generating an alert or notification based on a specific sequence of stock ticks.

If someone could post an example it would be greatly appreciated.

Thanks!


Solution

  • What you're looking for is the Reactive Parsers out of Rxx.

    This isn't F# but rather a .NET library that let's you write code such as (following from your stock example):

        var alerts = ticks.Parse(parser =>
        from next in parser
        let ups = next.Where(tick => tick.Change > 0)
        let downs = next.Where(tick => tick.Change < 0)
        let downAlert = from manyUps in ups.AtLeast(2).ToList()
                   from reversalDown in downs.NonGreedy()
                   where reversalDown.Change <= -11
                   select new StockAlert(manyUps, reversalDown)
        let upAlert = from manyDowns in downs.AtLeast(2).ToList()
                from reversalUp in ups.NonGreedy()
                where reversalUp.Change >= 21
                select new StockAlert(manyDowns, reversalUp)
        select downAlert.Or(upAlert).Ambiguous(untilCount: 1));
    

    Credit of course goes to Dave Sexton and James Miles who did the majority of this work.

    For background reading, the parser extensions to Rxx came out of this discussion: http://qa.social.msdn.microsoft.com/Forums/eu/rx/thread/0f72e5c0-1476-4969-92da-633000346d0d

    Here's a very simple example of how this could be used in F#:

    open Rxx.Parsers.Reactive
    open Rxx.Parsers.Reactive.Linq
    
    // F# shortcuts to Rxx
    let where f (a:IObservableParser<_,_>) = a.Where(fun b -> f b)
    let toList (parser:IObservableParser<_,_>) = parser.ToList()
    let (<&>) (a:IObservableParser<'a,'b>) (b:IObservableParser<'a,'b>) = a.And(b)
    let create a = 
        { new ObservableParser<_,_>() with
            override x.Start = a(x.Next) } :> IObservableParser<_,_>
    let parse (parser:IObservableParser<_,_>) (obs:IObservable<_>) = obs.Parse(parser)
    
    // example of grammar
    let grammar = 
        (fun (parser:IObservableParser<_,_>) ->
            let next = parser.Next
            let bigs = next |> where(fun i -> i > 25)
            let smalls = next |> where(fun i -> i <= 25)
            bigs <&> smalls |> toList ) 
        |> create
    
    // the test
    let random = Random()
    let values = Observable.Interval(TimeSpan.FromMilliseconds(500.0)).Select( fun _ -> random.Next(1,50)).Trace().TraceSubscriptions("subbing","subbed","disposing","disposed").Publish()
    
    let sub = values |> parse grammar |> Observable.add(printfn "BIG THEN SMALL: %A")
    let test = values.Connect()