作为示例,我将采用 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 在解析输入的开头显示错误?使用 >>?
、.>>.?
或任何回溯变体似乎没有效果。
attempt
,如果解析器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
>>=?
运算符,它显然(至少在语义上)等同于 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