提问人:Stelios Adamantidis 提问时间:3/31/2022 更新时间:3/31/2022 访问量:94
使用 FParsec,是否可以在解析器失败时操纵错误位置?
With FParsec is it possible to manipulate the error position when a parser fails?
问:
举个例子,我将以 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# 代码段,但为了示例的缘故,我使用 来解析 后面的文本。是否可以告诉 FParsec 在解析输入的开头显示错误?使用 或任何回溯变体似乎没有效果。pidentifier
font=
>>?
.>>.?
答:
1赞
Brian Berns
3/31/2022
#1
我认为您想要的是尝试 p
,如果解析器失败,它将回溯到原始解析器状态。因此,您可以将其定义为: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
更新
如果不想在错误消息中看到回溯信息,可以使用简化版本,如下所示: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
输出现在只是:
Failure:
Error in Ln: 1 Col: 1
default
^
keyword instead of identifier
评论
0赞
Brian Berns
3/31/2022
听到这个消息,我感到很惊讶。它对我来说是正确的。我已经用我看到的输出更新了我的答案。
0赞
Brian Berns
3/31/2022
是的,我认为是有效的加.就我个人而言,我总是使用 ,因为它更通用且更容易记住。(FWIW,我也使用计算构建器而不是手动编写绑定。>>=?
>>=
attempt
attempt
parse
>>=
0赞
Brian Berns
3/31/2022
我已经更新了我的答案,以包含一个简化版本,该版本不包括输出错误消息中的回溯信息。attempt
0赞
Stelios Adamantidis
4/4/2022
是的,就是这样。我把答案给了你——反正这是你的,你给了我的大脑食物,让我开始思考:)。我认为同样,我可以更改不那么冗长的内容。PS 我会在某个时候删除我的旧评论,因为它们不再有用了。>>=?
1赞
Stelios Adamantidis
3/31/2022
#2
不幸的是,我错过了 >>=?
运算符,它显然(至少在语义上)等同于 Brian Berns 正确建议的运算符。attempt
这两种方法的问题在于,如果前面的解析器也在回溯,则后续消息可能会级联: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
评论
0赞
Brian Berns
3/31/2022
是的,回溯信息可能在错误消息中很重要。不幸的是,我认为没有一种简单的方法可以删除它。
评论