如果“直到”解析器以空格开头,为什么 manyCharsTill 组合器不起作用?

Bal*_*nth 4 f# parser-combinators fparsec

我尝试解析类似 xml 的标签(但不是正确的 xml 文档..):目标是返回没有开头或结尾空格的“法兰宽度”,但带有内部空格。

open FParsec

let testParser =
    pstring "<desc>" .>>. spaces
    >>. manyCharsTill anyChar (spaces .>>. pstring "</desc>")

run testParser "<desc> Flange width </desc>"
Run Code Online (Sandbox Code Playgroud)

如果我理解解析器组合器的预期结果:

保持吞咽字符的 anyChar 解析器单元“直到”解析器,它寻找空格,然后是结束标记成功。

实际发生的情况是,“直到”解析器在“宽度”之前的空间上失败(应该如此),但会使 manyTill 解析器短路,而不是让 anyChar 吞下该空间并继续前进。

输出:

val it : ParserResult<string,unit> =
  Failure:
Error in Ln: 1 Col: 15
<desc> Flange width </desc>
              ^
Expecting: '</desc>'
Run Code Online (Sandbox Code Playgroud)

我没有得到什么?或者这里的惯用解决方案是什么?

Cha*_*ger 5

问题是spaces解析成功并将流移动到w. pstring "</desc>"然后失败。

最终结果是endp解析器失败了,但它改变了状态(我们已经移过了空格)。您希望解析器失败并且更改状态(在空格之前)。的文档manyTill(由 引用manyCharsTill)解释了这一点:

只要失败,解析器就会manyTill p endp重复应用解析器(不改变解析器状态)。pendp

您可以使用.>>.?运算符来做到这一点:

解析器的p1 .>>.? p2行为类似于p1 .>>. p2,除了如果p2失败并出现非致命错误并且不更改解析器状态,即使p1已更改解析器状态,它将回溯到开头。

所以这个:

let testParser =
    pstring "<desc>" .>>. spaces
    >>. manyCharsTill anyChar (spaces .>>.? pstring "</desc>")
Run Code Online (Sandbox Code Playgroud)

有关工作演示,请参阅此小提琴