Parsing 如何解决FParsec错误“;组合器';许多';已应用于成功而不使用…”的解析器;

Parsing 如何解决FParsec错误“;组合器';许多';已应用于成功而不使用…”的解析器;,parsing,f#,fparsec,Parsing,F#,Fparsec,我有一个语法分析器,看起来很简单。我在末尾添加了这个子解析器,以提供有关一般解析错误的信息,因为所有其他子解析器都失败了- /// Read the rest of a line as an error. let readError = parse { let! restOfLineStr = restOfLine true return makeViolation ("Read error on: " + restOfLineStr + ".") }

我有一个语法分析器,看起来很简单。我在末尾添加了这个子解析器,以提供有关一般解析错误的信息,因为所有其他子解析器都失败了-

/// 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错误-
组合符“many”被应用于一个解析器,该解析器在不消耗输入和不以任何其他方式更改解析器状态的情况下成功运行。
我不明白为什么会出现这种情况,因为我确实使用了解析后的行的其余部分来创建一个使用过的错误(这里是“违反”)结构

有人能帮我理解吗?我是否以错误的方式向用户发送解析器错误信号?如果没有,我如何解决这个问题

谢谢你的帮助

*更多详细信息*

这里还有一些可能相关的代码-

/// 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 }
现在,当我需要获取输入流中的所有表达式时,我只运行readExprsTillEnd


再次感谢你,古斯塔沃

感谢您发布的附加代码,不幸的是,我无法重现错误。 但是,为什么不尝试删除最后一次
尝试
?我认为这毫无意义,可能是造成了一个问题

do readExprRef :=
    choice
        [attempt readBoolean
         attempt readCharacter
         attempt readString
         attempt readInt
         readError]
我不是FParsec专家,但我认为最后一个选择的解析器不应该是尝试

更新:

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

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

看看restOfLine函数规范,它警告您这一点

现在有很多方法可以解决这个问题,但是我想说,您必须重新考虑处理解析器错误的方法

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

let readExprs = many readExpr .>> eof
这样做可以强制执行eof,如果在eof之前的选择中有一些东西没有被解析器处理,FParsec将自动为您生成一条漂亮的错误消息


如果您想处理该错误,请查看

是否可以发布更多代码?特别是makeViolation函数的主体.Done。顺便说一句,古斯塔沃,我是你博客的超级粉丝。我希望Don Syme正在关注您所做的工作-在没有HKs或TCs的情况下获得函子、应用程序和单子是巨大的!我想知道我们是否也能从你那里得到某种形式的箭;)感谢Bryan对我的TypeClass工作的反馈。我还希望F#团队的人员能够为这项技术添加更多支持,否则他们将依赖CLR团队在.NET级别实现真正的TypeClass支持。关于箭,你看了吗?上的项目中有更多关于箭头的内容。我删除了上次的“尝试”,但它仍然具有相同的行为:(你介意我亲自打电话给你吗?如果不介意,请将你的联系信息滑到bryanedds@yahoo.com.干杯!是的,这很有效!我在你的大力帮助下完善了答案中的最终解决方案!非常感谢!
let readExprs = many readExpr .>> eof