简体   繁体   English

使用 FParsec 是否可以在解析器失败时处理错误 position?

[英]With FParsec is it possible to manipulate the error position when a parser fails?

As an example, I will take this simple C# parser by Phillip Trelford.作为示例,我将以 Phillip Trelford 的这个简单的 C# 解析器为例。 In order to parse an identifier he writes this (slightly changed):为了解析一个标识符,他写了这个(略有改动):

let reserved = ["for";"do"; "while";"if";"switch";"case";"default";"break" (*;...*)]
let pidentifierraw =
    let isIdentifierFirstChar c = isLetter c || c = '_'
    let isIdentifierChar c = isLetter c || isDigit c || c = '_'
    many1Satisfy2L isIdentifierFirstChar isIdentifierChar "identifier"
let pidentifier =
    pidentifierraw
    >>= fun s ->
        if reserved |> List.exists ((=) s) then fail "keyword instead of identifier"
        else preturn s

The problem with pidentifier is that when it fails, the position indicator is at the end of the stream. An example of mine: pidentifier 的问题在于,当它失败时,position 指标位于 stream 的末尾。我的一个例子:

Error in Ln: 156 Col: 41 (UTF16-Col: 34)
        Block "main" 116x60 font=default fg=textForeground
                                        ^
Note: The column count assumes a tab stop distance of 8 chars.
keyword instead of identifier

Obviously, not a C# snippet, but for the example's sake, I've used the pidentifier to parse the text after font= .显然,不是 C# 片段,但为了示例的缘故,我使用了pidentifier来解析font=之后的文本。 Is it possible to tell FParsec to show the error at the beginning of the parsed input?是否可以告诉 FParsec 在解析输入的开头显示错误? Using >>?使用>>? , .>>.? , .>>.? or any of the backtracking variants seems to have no effect.或任何回溯变体似乎都没有效果。

I think what you want is attempt p , which will backtrack to the original parser state if parser p fails.我想你想要的是attempt p ,如果解析器p失败,它将回溯到原始解析器 state 。 So you could just define pidentifier as:所以你可以将pidentifier定义为:

let pidentifier =
    pidentifierraw
    >>= fun s ->
        if reserved |> List.exists ((=) s) then fail "keyword instead of identifier"
        else preturn s
    |> attempt   // rollback on failure

Output is then something like: Output 是这样的:

Failure:
Error in Ln: 1 Col: 1
default
^

The parser backtracked after:
  Error in Ln: 1 Col: 8
  default
         ^
  Note: The error occurred at the end of the input stream.
  keyword instead of identifier

Update更新

If you don't want to see the backtracking info in the error message, you can use a simplified version of attempt , like this:如果您不想在错误消息中看到回溯信息,您可以使用简化版本的attempt ,如下所示:

let attempt (parser : Parser<_, _>) : Parser<_, _> =
    fun stream ->
        let mutable state = CharStreamState(stream)
        let reply = parser stream
        if reply.Status <> Ok then
            stream.BacktrackTo(&state)
        reply

Output is now just: Output 现在只是:

Failure:
Error in Ln: 1 Col: 1
default
^
keyword instead of identifier

Unfortunately I missed the >>=?不幸的是我错过了>>=? operator which apparently is (at least semantically) equivalent to attempt that Brian Berns correctly suggested .显然(至少在语义上)等同于attempt that Brian Berns correctly suggested的运算符。

The problem with both of these approaches is the subsequent messages The parser backtracked after:[…] that can cascade if the preceding parsers are backtracking as well:这两种方法的问题是后续消息The parser backtracked after:[…]如果前面的解析器也在回溯,则可以级联:

Error in Ln: 156 Col: 29 (UTF16-Col: 22)
        Block "main" 116x60 font=default fg=textForeground
                            ^
Note: The column count assumes a tab stop distance of 8 chars.
Expecting: space/tab

The parser backtracked after:
  Error in Ln: 156 Col: 42 (UTF16-Col: 35)
          Block "main" 116x60 font=default fg=textForeground
                                           ^
  Note: The column count assumes a tab stop distance of 8 chars.
  Expecting: space/tab
  Other error messages:
    keyword instead of identifier

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM