2013-05-19 83 views
3

我有這個測試程序:FParsec沒有很多

open FParsec 

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

let str s = pstring s 

let sepPart = skipNewline >>. pstring "-" 

let part = manyChars (notFollowedBy sepPart >>. anyChar) 

[<EntryPoint>] 
let main argv = 
    let s = "AA 12345\nBB 6789\n-----\nCC 9876\nDD 54321\n-----" 
    test part s 
    test (many part) s 

    0 // return an integer exit code 

線{測試部分S}按預期工作,但下一行,{測試(多部分)S}失敗,我不明白我做錯了什麼。

編輯:

爲了澄清,我試圖做的是有{測試(多部分)S}返回[ 「AA 12345 \ NBB 6789」; 「CC 9876 \ nDD 54321」]。換句話說,我所擁有的是由「pars」或「chunk」組成的輸入字符串,並用所有破折號分隔。對於輸出我想要一個數組,其中每個元素是其中一個部分,並且帶有破折號的行只是被丟棄。

回答

8

當執行你的例子,FParsec投用下面的消息的異常:

其他信息:(LN:2,柱:8):該組合子「多」施加到解析器 那成功消除輸入,並且不以任何其他方式更改解析器狀態。 (如果沒有異常已經 提出,該組合子可能會進入一個無限循環)。

的問題是,你的part解析器總是成功,即使它只能解析一個空字符串。您可以通過將partmanyChars替換爲many1Chars來解決該問題。

如果您搜索例如「適用於成功,不消耗輸入分析器」,你會發現類似的錯誤在互聯網上多次討論,其中包括在FParse的用戶指南:http://www.quanttec.com/fparsec/users-guide/parsing-sequences.html#the-many-parser

更新: 這裏是一個簡單的解析器定義,工作原理:

let sepPart = skipNewline 
       >>? (skipMany1SatisfyL ((=) '-') "'-'" 
        >>. (skipNewline <|> eof)) 

let part = many1CharsTill anyChar sepPart  
let parser = many part 

請注意,我在sepPart定義使用>>?允許,如果一個新行後面沒有一個破折號這個分析器回溯到年初。或者,您也可以使用attempt (skipNewline >>. ...),它也會在最初的短劃線後面出現錯誤。 many[Chars]Till p endp的文檔聲明與many (notFollowedBy endp >>. p) .>> endp等效,但這不完全正確,因爲many[Chars]Till不會像notFollowedBy那樣回溯。我會澄清文件。

如果在可能的情況下避免使用many[Chars]TillnotFollowedBy回溯,性能會更好。例如,你也可以解析你的線條的塊如下:

let id = manyMinMaxSatisfyL 2 2 isUpper "id (two capital letters)" 

let line = id .>>. (pchar ' ' >>. restOfLine true) 

let separator = many1SatisfyL ((=) '-') "dash separator" 
       >>. (skipNewline <|> eof) 

let chunk = many1 line  
let parser = sepEndBy1 chunk separator 

注意,此實現不需要最後一個塊由一個分隔符結束。如果你想要的,你也可以使用:

let chunk = many line .>> separator 
let parser = many chunk 

如果要允許與sepEndBy定義空塊,你可以使用:

let chunk = many1 line <|> (notFollowedByEof >>% []) 
let parser = sepEndBy1 chunk separator 
+0

這不會取消錯誤,但它不會返回正確的結果。如果您查看第一個解析{test part}所返回的內容,那麼當部分解析器中的manyChars更改爲many1Chars時,您將看到結果更改。 – JonnyBoats

+0

我沒有看到區別,除了'部分'返回一個字符串和'很多部分'返回一個列表中的字符串。你能否更具體一些,並解釋你期望的輸出?你的解析器永遠不會跳過'sepPart',所以你可能想要'many(part。>> sepPart)''。如果你想匹配分隔符的一個或多個破折號,你還必須使用'skipMany1(pstring「 - 」)'或'skipMany1SatisfyL((=)' - ')「' - '」' 'sepPart'的定義。 –

+0

我編輯了這個問題以更好地描述預期的輸出。我認爲沒有更清楚地開始。 – JonnyBoats