如何解决 FParsec 错误“组合器‘许多’已应用于解析器,该解析器在不消耗...的情况下成功”

2024-05-23

我有一个看起来足够简单的解析器。我将此子解析器添加到末尾以提供有关一般解析错误的信息,因为所有其他子解析器都失败了 -

/// 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 即可。

再次感谢,古斯塔沃!


感谢您发布的附加代码,不幸的是我无法重现该错误。 但你为什么不尝试删除最后一个attempt?我认为这没有意义,而且可能会引起问题。

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

我不是 FParsec 专家,但我认为最后一个解析器的选择不应该是一种尝试。

UPDATE:

The readError如果在某个时刻您调用了,即使不消耗任何输入,解析器也会成功readExpr作为 a 的参数many它永远不会结束。 我的意思是如果你打电话

run (many readError) "" ;;

您会收到该错误消息,因为many将继续应用该解析器直到它失败,但它永远不会失败。

查看restOfLine 函数规范:http://www.quanttec.com/fparsec/reference/charparsers.html#members.restOfLine http://www.quanttec.com/fparsec/reference/charparsers.html#members.restOfLine它会警告您这一点。

现在有很多方法可以解决它,但我想说你必须重新考虑处理解析器错误的方式。

你可以做的一件事就是取出readError函数,然后当你调用 readExpr 解析器你这样称呼它

let readExprs = many readExpr .>> eof

这样做可以强制执行 eof,如果在 eof 之前的选择中解析器未处理某些内容,FParsec 将自动为您生成一条不错的错误消息。

如果您想处理该错误,请查看http://www.quanttec.com/fparsec/users-guide/customizing-error-messages.html http://www.quanttec.com/fparsec/users-guide/customizing-error-messages.html

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何解决 FParsec 错误“组合器‘许多’已应用于解析器,该解析器在不消耗...的情况下成功” 的相关文章

随机推荐