F# 使用FParsec解析可选的多行表达式

F# 使用FParsec解析可选的多行表达式,f#,fparsec,F#,Fparsec,我正在为以下形式的字符串编写FParsec解析器: do[ n times]([ action] | \n([action]\n)*endDo) 换句话说,这是一个带有可选时间量词的“do”语句,或者是一个“action”语句,或者是一个在末尾带有“end do”的“action”列表(每一个都在新行上)(为了简单起见,我省略了缩进/尾随空格处理) 以下是有效输入的示例: do action do 3 times action do endDo do 3 times endDo do

我正在为以下形式的字符串编写FParsec解析器:

do[ n times]([ action] | \n([action]\n)*endDo)
换句话说,这是一个带有可选时间量词的“do”语句,或者是一个“action”语句,或者是一个在末尾带有“end do”的“action”列表(每一个都在新行上)(为了简单起见,我省略了缩进/尾随空格处理)

以下是有效输入的示例:

do action

do 3 times action

do
endDo

do 3 times
endDo

do
action
action
endDo

do 3 times
action
action
endDo
这看起来并不复杂,但:

为什么这不起作用?

let statement = pstring "action"
let beginDo = pstring "do"
                >>. opt (spaces1 >>. pint32 .>> spaces1 .>> pstring "times")
let inlineDo = tuple2 beginDo (spaces >>. statement |>> fun w -> [w])
let expandedDo = (tuple2 (beginDo .>> newline)
                    (many (statement .>> newline)))
                 .>> pstring "endDo"
let doExpression = (expandedDo <|> inlineDo)
let语句=pstring“action”
让beginDo=pstring“do”
>>. 选项(空格1>>.pint32.>>空格1.>>pstring“时间”)
让inlineDo=tuple2 beginDo(空格>>.statement |>>fun w->[w])
让expandedDo=(tuple2(beginDo.>>newline)
(许多(声明>>newline)))
.>>pstring“endDo”
让doExpression=(expandedDo inlineDo)

此表达式的正确解析器是什么?

您需要使用
尝试
函数。
let statement = pstring "action"
let beginDo = pstring "do"
                >>. opt (spaces1 >>. pint32 .>> spaces1 .>> pstring "times")
let inlineDo = tuple2 beginDo (spaces >>. statement |>> fun w -> [w])
let expandedDo = (tuple2 (beginDo .>> newline)
                    (many (statement .>> newline)))
                 .>> pstring "endDo"
let doExpression = (expandedDo <|> inlineDo)
我刚刚修改了您的
beginDo
doExpression
函数

代码如下:

let statement  o=o|>  pstring "action"

let beginDo o= 
    attempt (pstring "do"
        >>. opt (spaces1 >>. pint32 .>> spaces1 .>> pstring "times")) <|> 
        (pstring "do" >>% None)                                       <|o

let inlineDo   o= tuple2 beginDo (spaces >>. statement |>> fun w -> [w]) <|o
let expandedDo o= (tuple2 (beginDo .>> newline) (many (statement .>> newline)))
                 .>> pstring "endDo" <|o

let doExpression o= ((attempt expandedDo) <|> inlineDo) .>> eof <|o
let语句o=o |>pstring“action”
让我们开始o=
尝试(pstring“do”
>>.opt(spaces1>>.pint32.>>spaces1.>>pstring“时间”))
(pstring“do”>>%None)>。语句|>>fun w->[w])>newline(许多(语句>>newline)))

.>>pstring“endDo”>eof有效!所以基本上我错过了两次
尝试
?我仍然不明白为什么需要它们。是的,因为如果第一个解析器(在p1p2表达式中)失败,并且它已经消耗了输入,那么您需要一种回溯的方法。这就是尝试函数的基本功能。好的,明白了。但是什么时候你可以不用尝试就使用它呢?每次你做b时,a必须失败才能选择b,不是吗?例如,如果你做了pFloat某事,你不需要它,因为如果pFloat失败,它不会消耗任何输入。问题是,当您编写解析器时,可能最后一个解析器失败了,但其他解析器消耗了一些输入<代码>空格
也使用换行符,因此解析器将接受不带“endDo”的“do[newline]action”,这可能在以后成为一个gotcha。