I'm trying to write a custom attempt
parser in FParsec. I tried it, but I'm an F# / FParsec beginner and get lost with the F# compiler errors I produce. This is my best guess so far:
我正在尝试用FParsec编写一个定制的尝试解析器。我试过,但我是一个F#/FParsec初学者,我产生的F#编译器错误让我迷失了方向。这是我到目前为止最好的猜测:
let customAttempt (p: CharStream<State> -> ParserResult<SyntaxNode,State>) : Parser<SyntaxNode,State> =
fun (stream: CharStream<State>) ->
let previousState = stream.State
let (result, diagnostics) = p stream //This causes Error FS0001 This expression was expected to have type ''a * 'b' but here has type 'ParserResult<SyntaxNode,State>'
match diagnostics with
| [] -> result
| _ ->
stream.BacktrackTo previousState
result //Here I actually want to return a result associated with previousState
Why would I ever need to write a custom attempt parser?
为什么我需要编写一个定制的尝试解析器呢?
Because I want to attempt parsers that never fail. In the FParsec attempt documentation, they say
因为我想尝试永远不会失败的解析器。在FParsec尝试文档中,他们说
The parser attempt p
applies the parser p
. If p
fails after changing the parser state or with a fatal error, attempt p
will backtrack to the original parser state and report a non‐fatal error.
In my case, the parser p
never fails. What I'm currently implementing is an error recovery for my parser based on the approach described in this block post. This approach is to construct a parser p
that never fails. Instead, it would always return a Parser<SyntaxNode,State>
value, where SyntaxNode
is a type for an optional parser result, and State
is a list of Diagnostics
. In case this list is empty, the parser p
was successful.
在我的例子中,解析器p从未失败过。我目前正在实现的是基于本文中描述的方法的解析器的错误恢复。这种方法是构造一个永不失败的解析器p。相反,它将始终返回Parser
值,其中SynaxNode是可选解析器结果的类型,State是诊断列表。如果该列表为空,则解析器p成功。
The mentioned approach to error recovery in FParsec works well for simple grammars. My grammar is more complex and involves choice
, attempt
, or skipManyTill
parsers. Since p
never fails in my case, these original FParsec parses do not do the trick anymore.
前面提到的FParsec中的错误恢复方法适用于简单的语法。我的语法更复杂,涉及选择、尝试或跳过ManyTill解析器。由于p在我的例子中从未失败过,所以这些原始的FParsec解析不再起作用。
Therefore, I'm trying to write my own versions of customChoice
, customAttempt
, or customSkipManyTill
, so that I can use them for my grammar that would also support error recovery based on the mentioned approach of parsers that never fail but emit diagnostics instead.
因此,我尝试编写我自己的customChoice、customAttempt或customSkipManyTill版本,以便我可以将它们用于我的语法,该语法还支持基于上述解析器方法的错误恢复,这些解析器从不失败,而是发出诊断。
更多回答
优秀答案推荐
This is an interesting idea. I'd be curious to know how it turns out.
这是一个有趣的想法。我很想知道结果如何。
I think the problem with your code is that you're assuming that a parser returns a (result, diagnostics)
tuple, but this is not correct. Diagnostics are actually stored in the stream state, not returned by the parser. I think you want to check the return value of the parser to determine whether to backtrack, so the code should probably look something like this:
我认为您代码的问题在于您假设解析器返回一个(结果、诊断)元组,但这是不正确的。诊断实际上存储在流状态中,而不是由解析器返回。我认为您需要检查解析器的返回值以确定是否回溯,因此代码可能如下所示:
let customAttempt (p : Parser<SyntaxNode, _>) =
fun (stream : CharStream<_>) ->
let state = stream.State
let reply = p stream
if reply.Result = SyntaxNode.Error then
stream.BacktrackTo(state)
reply
Note that I haven't tried this, so I don't know if it works. Good luck!
请注意,我还没有尝试过这个,所以我不知道它是否有效。祝好运!
更多回答
Thank you. It solved my f# compiler problem but not my core problem. I need some more investigation into this to follow up.
谢谢。它解决了我的f#编译器问题,但没有解决我的核心问题。我需要更多的调查来跟进这件事。
我是一名优秀的程序员,十分优秀!