[英]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
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.