2013-07-26 56 views
2

據我所知,選項組合子隱式地將pzero解析器附加到我的解析器列表中,並且當fparsec未能解析輸入流的下一部分時,它應該搜索括號。爲什麼combinator「between」不適用於「choice」作爲應用分析器?

這裏是最小的完整代碼:

open System 
open System.Collections.Generic 
open FParsec 

type IDL = 
    |Library of string * IDL list 
    |ImportLib of string 
    |ImportAlias of string 

let comment : Parser<unit,unit> = pstring "//" >>. skipRestOfLine true >>. spaces 
let ws = spaces >>. (opt comment) 
let str s = pstring s >>. ws 
let identifierString = ws >>. many1Satisfy isLetter .>> ws // [A-z]+ 
let identifierPath = ws >>. many1Satisfy (fun c -> isLetter c || isDigit c || c = '.' || c = '\\' || c = '/') .>> ws // valid path letters 
let keywords = ["importlib"; "IMPORTLIB"; "interface"; "typedef"; "coclass"] 
let keywordsSet = new HashSet<string>(keywords) 
let isKeyword (set : HashSet<string>) str = set.Contains(str) 

let pidentifier set (f : Parser<string, unit>) : Parser<string, unit> = 
    let expectedIdentifier = expected "identifier" 
    fun stream -> 
     let state = stream.State 
     let reply = f stream 
     if reply.Status <> Ok || not (isKeyword set reply.Result) then 
      printf "got id %s\n" reply.Result 
      ws stream |> ignore 
      reply 
     else // result is keyword, so backtrack to before the string 
      stream.BacktrackTo(state) 
      Reply(Error, expectedIdentifier) 

let identifier = pidentifier keywordsSet 

let stmt, stmtRef = createParserForwardedToRef() 

let stmtList = sepBy1 stmt (str ";") 

let importlib = 
    str "importlib" >>. 
     between (str "(" .>> str "\"") (str "\"" >>. str ")") 
      (identifier identifierPath) |>> ImportLib 

let importalias = 
    str "IMPORTLIB" >>. 
     between (str "(") (str ")") 
      (identifier identifierString) |>> ImportAlias 

let library = 
    pipe2 
     (str "library" >>. identifier identifierString) 
     (between (str "{") (str "}") stmtList) 
     (fun lib slist -> Library(lib, slist)) 

do stmtRef:= choice [importlib; importalias] 

let prog = 
    ws >>. library .>> ws .>> eof 

let s = @" 
library ModExpress 
{ 
    importlib(""stdole2.tlb""); 
    importlib(""msxml6.dll""); 
}" 

let test p str = 
    match run p str with 
    | Success(result, _, _) -> printfn "Success: %A" result 
    | Failure(errorMsg, _, _) -> printfn "Failure: %s" errorMsg 

test prog s 

System.Console.Read() |> ignore 

但對於輸入字符串

library ModExpress 
{ 
     importlib(""stdole2.tlb""); 
     importlib(""msxml6.dll""); 
} 

我得到了以下錯誤:

Failure: Error in Ln: 6 Col: 1 
} 
^ 
Expecting: '//', 'IMPORTLIB' or 'importlib' 

回答

3

看來,這裏的問題是, stmtList解析器與sepBy1組合器一起實現。 sepBy1 stmt sep解析p分離(但不結束)sep,即在EBNF:p(sep p)*中的一個或多個事件。當解析器在importlib(「」msxml6.dll「」)後面看到分號時,它需要在空白符之後的另一個語句。

如果你想在語句列表可選的最後的分號,你可以簡單地使用sepEndBy1代替sepBy1,或者如果你總是想要求一個分號,你可以使用

let stmtList = many1 stmt 

do stmtRef:= choice [importlib; importalias] .>> str ";" 
+0

謝謝,Stephan - 它與sepEndBy1完美配合 – ssp

相關問題