2011-12-17 20 views
5

我有一個解析器,似乎足夠直接。我加入這個子解析器年底提供有關一般分析錯誤信息,因爲所有其他子解析器失敗 -如何解決FParsec錯誤「組合器'很多'被應用於成功解析器,而不消耗...」

/// Read the rest of a line as an error. 
let readError = 
    parse { 
     let! restOfLineStr = restOfLine true 
     return makeViolation ("Read error on: " + restOfLineStr + ".") } 

/// Read an expression. 
do readExprRef := 
    choice 
     [attempt readBoolean 
     attempt readCharacter 
     attempt readString 
     attempt readInt 
     attempt readError] // just now added this sub-parser, and get the issue 

但是,一旦我添加readError作爲一個選擇,我得到流可怕FParsec錯誤在運行時消耗 - The combinator 'many' was applied to a parser that succeeds without consuming input and without changing the parser state in any other way.我不明白爲什麼我得到這個,因爲我使用解析的行剩餘來創建一個使用的錯誤(這裏'違反')結構。

有人可以幫我理解嗎?我是否以錯誤的方式向用戶發送解析器錯誤?如果沒有,我該如何解決這個問題?

謝謝你的幫助!

*更多細節*

這裏的一些代碼,可能是相關的 -

/// The expression structure. 
type Expr = 
| Violation of Expr 
| Boolean of bool 
| Character of char 
| String of string 
| Int of int 

/// Make a violation from a string. 
let makeViolation str = Violation (String str) 

/// Read whitespace character as a string. 
let spaceAsStr = anyOf whitespaceChars |>> fun chr -> string chr 

/// Read a line comment. 
let lineComment = pchar lineCommentChar >>. restOfLine true 

/// Read a multiline comment. 
/// TODO: make multiline comments nest. 
let multilineComment = 
    between 
     (pstring openMultilineCommentStr) 
     (pstring closeMultilineCommentStr) 
     (charsTillString closeMultilineCommentStr false System.Int32.MaxValue) 

/// Read whitespace text. 
let whitespace = lineComment <|> multilineComment <|> spaceAsStr 

/// Skip any white space characters. 
let skipWhitespace = skipMany whitespace 

/// Skip at least one white space character. 
let skipWhitespace1 = skipMany1 whitespace 

/// Read a boolean. 
let readBoolean = 
    parse { 
     do! skipWhitespace 
     let! booleanValue = readStr trueStr <|> readStr falseStr 
     return Boolean (booleanValue = trueStr) } 

/// Read a character. 
let readCharacter = 
    parse { 
     // TODO: enable reading of escaped chars 
     do! skipWhitespace 
     let! chr = between skipSingleQuote skipSingleQuote (manyChars (noneOf "\'")) 
     return Character chr.[0] } 

/// Read a string. 
let readString = 
    parse { 
     // TODO: enable reading of escaped chars 
     do! skipWhitespace 
     let! str = between skipDoubleQuote skipDoubleQuote (manyChars (noneOf "\"")) 
     return String str } 

/// Read an int. 
let readInt = 
    parse { 
     do! skipWhitespace 
     let! value = pint32 
     let! _ = opt (skipString intSuffixStr) 
     do! notFollowedByLetterOrNameChar 
     do! notFollowedByDot 
     return Int value } 

我不知道。可能問題在於,一旦它嘗試運行readError解析器,它就已經在流的末尾了。這將使restOfLine不消耗任何輸入,甚至沒有空白?

* *結論

事實證明,該方法錯誤與readError解析器報告是錯誤的。正確的做法是使用「直到結束」解析器像這樣 -

/// Read the end of input. 
let readEndOfInput = skipWhitespace >>. eof 

// Read multiple exprs. 
let readExprs = many readExpr 

// Read exprs until the end of the input. 
let readExprsTillEnd = readExprs .>> readEndOfInput 

現在我只是運行readExprsTillEnd當我需要得到所有的exprs輸入流。

再次感謝Gustavo!

+0

你可以發佈更多的代碼?特別是makeViolation函數的主體。 – Gustavo 2011-12-17 09:17:30

+0

完成。順便說一句,古斯塔沃,我是你博客的粉絲。我希望Don Syme正在研究你所做的工作 - 在沒有HK或TC的情況下獲得功能,應用程序和monad是巨大的!我想知道我們是否也能以某種形式從你那裏得到箭頭;) – 2011-12-17 14:38:35

回答

1

感謝您發佈的附加代碼,很遺憾,我無法重現錯誤。 但是,你爲什麼不嘗試刪除最後的attempt?我認爲這沒有意義,也許是造成問題。

do readExprRef := 
    choice 
     [attempt readBoolean 
     attempt readCharacter 
     attempt readString 
     attempt readInt 
     readError] 

我不是一個FParsec專家,但我認爲選擇的最後一個解析器不應該是一個嘗試。

UPDATE:

readError解析器成功甚至不消耗輸入,如果在某些時候你必須調用readExprmany它永遠不會結束的參數。 我的意思是,如果你打電話

run (many readError) "" ;; 

你會得到錯誤信息,因爲many將繼續應用該解析器,直到它失敗了,但它永遠不會失敗。

看看http://www.quanttec.com/fparsec/reference/charparsers.html#members.restOfLine的restOfLine函數規範,它會提醒你。

現在有很多方法可以解決它,但我會說你將不得不重新考慮你處理解析器錯誤的方式。

一兩件事你可以做的是,當你調用readExpr解析器你這樣調用它

let readExprs = many readExpr .>> eof 

這樣做,你強制EOF,如果有什麼不被處理,取出readError功能,然後解析器在選擇eof之前,FParsec會自動爲你生成一個很好的錯誤信息。

如果你要處理的錯誤,看看http://www.quanttec.com/fparsec/users-guide/customizing-error-messages.html