使用FPARSEC,当解析器失败时是否可以操纵误差位置?

发布于 2025-01-18 01:41:13 字数 1114 浏览 0 评论 0原文

作为示例,我将采用 Phillip Trelford 的简单的 C# 解析器。为了解析标识符,他这样写(稍作修改):

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

pidentifier 的问题是,当它失败时,位置指示器位于流的末尾。我的一个例子:

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

显然,不是 C# 代码片段,但为了示例起见,我使用 pidentifier 来解析 font= 之后的文本。是否可以告诉 FParsec 在解析输入的开头显示错误?使用 >>?.>>.? 或任何回溯变体似乎没有效果。

As an example, I will take this simple C# parser by Phillip Trelford. 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:

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=. Is it possible to tell FParsec to show the error at the beginning of the parsed input? Using >>?, .>>.? or any of the backtracking variants seems to have no effect.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

ι不睡觉的鱼゛ 2025-01-25 01:41:13

我认为你想要的是 attempt p< /a>,如果解析器p失败,它将回溯到原始解析器状态。因此,您可以将 pidentifier 定义为:

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

输出类似于:

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

如果您不想在错误消息中看到回溯信息,您可以使用 attempt< 的简化版本/code>,像这样:

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

输出现在只是:

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

I think what you want is attempt p, which will backtrack to the original parser state if parser p fails. So you could just define pidentifier as:

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:

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:

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:

Failure:
Error in Ln: 1 Col: 1
default
^
keyword instead of identifier
清风无影 2025-01-25 01:41:13

不幸的是,我错过了&gt;&gt; =? 操作员显然(至少在语义上)等于尝试 Brian Berns正确建议

这两种方法的问题是随后的消息在以下方式回溯:[…],如果前面的解析器也在回溯:

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

Unfortunately I missed the >>=? operator which apparently is (at least semantically) equivalent to 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:

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
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文