如何解决FParsec错误“将组合器“很多"应用于成功但不消耗...的解析器". [英] How to resolve FParsec error "The combinator 'many' was applied to a parser that succeeds without consuming..."

查看:104
本文介绍了如何解决FParsec错误“将组合器“很多"应用于成功但不消耗...的解析器".的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个看起来很简单的解析器.我在最后添加了此子解析器,以提供有关常规解析错误的信息,因为所有其他子解析器均失败-

I have a parser that seems straight-forward enough. I added this sub-parser to the end to give info about general parsing errors since all the other sub-parsers failed -

/// 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.我不明白为什么得到这个错误,因为我确实使用行的其余部分来解析创建一个使用过的错误(此处为违规")结构.

However, once I add readError as a choice, I get the dreaded FParsec error about stream consumption at run-time - The combinator 'many' was applied to a parser that succeeds without consuming input and without changing the parser state in any other way. I don't understand why I get this since I do use the parsed rest of the line to create a used error (here 'violation') structure.

有人可以帮助我理解这一点吗?我要以错误的方式向用户发送解析器错误吗?如果没有,我该如何解决?

Can someone help me understand this? Am I going about signaling parser errors to the user in the wrong way? If not, how could I fix this?

非常感谢您的帮助!

*详细信息*

还有更多可能与之相关的代码-

Here's some more code that may be relevant -

/// 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不消耗任何输入,甚至不占用空格吗?

I dunno. Maybe the issue is that it's already at the end of the stream once it tries to run the readError parser. Would that make restOfLine consume no input, not even whitespace?

*结论*

事实证明,使用readError解析器进行错误报告的方法是错误的.正确的方法是像这样使用耕种结束"解析器-

It turns out that the approach to error reporting with a readError parser is wrong. The correct approach is to use a 'till end' parser like so -

/// 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

现在,当我需要在输入流中获取所有exprs时,只需运行readExprsTillEnd.

Now I just run readExprsTillEnd when I need to get all the exprs in an input stream.

再次感谢,古斯塔沃!

推荐答案

感谢您发布的其他代码,很遗憾,我无法重现该错误. 但是,为什么不尝试删除最后一个attempt?我认为这没有任何意义,可能会引起问题.

Thanks for the additional code you posted, unfortunately I was unable to reproduce the error. But why don't you try to remove the last attempt? I think it makes no sense and maybe is causing a problem.

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

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

I'm not an FParsec expert but I think the last parser of a choice should not be an attempt.

更新:

即使不使用任何输入,readError解析器也会成功执行,如果在某个时候调用readExpr作为many的参数,它将永远不会结束. 我的意思是如果你打电话

The readError parser succeeds even consuming no input, if at some point you have a call to readExpr as parameter of a many it would never ends. I mean if you call

run (many readError) "" ;;

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

You'll get that error message because many will continue applying that parser until it fails, but it will never fail.

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

现在有很多方法可以解决它,但是我要说,您将不得不重新考虑处理解析器错误的方式.

Now there are many ways you can solve it, but I would say you will have to reconsider the way you handle the parser errors.

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

One thing you can do is take out the readError function and then when you call the readExpr parser you call it this way

let readExprs = many readExpr .>> eof

这样做是为了强制执行eof,如果在eof之前的选择中解析器无法处理某些事情,FParsec会自动为您生成一条不错的错误消息.

doing so you enforce the eof and if there is something not handled by the parsers in the choice before the eof, FParsec will automatically generate a nice error message for you.

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

And if you want to handle that error, have a look at http://www.quanttec.com/fparsec/users-guide/customizing-error-messages.html

这篇关于如何解决FParsec错误“将组合器“很多"应用于成功但不消耗...的解析器".的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆