2013-07-21 91 views
2

說我有一些文字:FParsec - 解析器序列

a = "foobarbaz" 
b = "foobar" 
c = "foo" 
d = "rubbish" 
e = "foobazbar" 

和三個解析器富,酒吧和巴茲爲字符串「富」,分別爲「酒吧」和「巴茲」。

我將如何創建一個解析器,這將使我的結果:

a = ["foo", "bar", "baz"] 
b = ["foo", "bar"] 
c = ["foo"] 
d = [] 
e = ["foo"] 

時對輸入上面跑?基本上嘗試每種可能性,直到構建列表失敗。我可以使用用戶狀態,但我希望儘可能避免。 (我想保持獨立的解析器自己無知的用戶狀態的)

我得到的最接近的是類似下面fooseq:

let foo = pstring "foo" 
let bar = pstring "bar" 
let baz = pstring "baz" 

let foobar = pipe2 foo bar Seq.of2 
let foobarbaz = pipe3 foo bar baz Seq.of3 

let fooseq = choice (Seq.map attempt [foobarbaz; foobar; foo |>> Seq.of1 ;]) 

//(the Seq.ofx functions just take arguments and create a sequence of them)  

在我看來,必須有這樣做的更好的辦法?

回答

5

FParsec沒有內置序列組合子,做你正在尋找什麼,但你可以自己實現一個類似下面的例子:

let mySeq (parsers: seq<Parser<'t,'u>>) : Parser<'t[],'u> = 
    let ps = Array.ofSeq parsers 
    if ps.Length = 0 then preturn [||] 
    else 
    fun stream -> 
     let mutable stateTag = stream.StateTag 
     let mutable reply = ps.[0] stream 
     let mutable error = reply.Error 
     let mutable myReply = Reply() 
     if reply.Status <> Ok then myReply.Result <- [||] 
     else 
     // create array to hold results 
     let mutable xs = Array.zeroCreate ps.Length 
     xs.[0] <- reply.Result 
     let mutable i = 1 
     while i < ps.Length do      
      stateTag <- stream.StateTag 
      reply <- ps.[i] stream 
      error <- if stateTag <> stream.StateTag then reply.Error 
        else mergeErrors error reply.Error 
      if reply.Status = Ok then 
      xs.[i] <- reply.Result 
      i <- i + 1 
      else // truncate array and break loop   
      xs <- Array.sub xs 0 i 
      i <- ps.Length 
     myReply.Result <- xs 
     myReply.Status <- if reply.Status = Error && stateTag = stream.StateTag 
         then Ok 
         else reply.Status 
     myReply.Error <- error    
     myReply 

隨着mySeq組合子,你可以表達你的fooSeq parser as

let fooSeq = mySeq [foo; bar; baz]