如果 "till" 解析器以空格开头,为什么 manyCharsTill 组合子不起作用?

Why doesn't manyCharsTill combinator work if the "till" parser starts with spaces?

我尝试解析类似 xml 的标签(但不是正确的 xml 文档..): 目标是只返回 "Flange width",没有开头或结尾的白色 space,但有内部的。

open FParsec

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

run testParser "<desc> Flange width </desc>"

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

anyChar 解析器保持吞咽字符单元 "till" 解析器查找 spaces 后跟结束标记成功。

实际发生的是,"till" 解析器在 "width" 之前的 space 上失败(它应该如此)但是短路 manyTill 解析器而不是让 anyChar 吞下 space 并继续前进。

输出:

val it : ParserResult<string,unit> =
  Failure:
Error in Ln: 1 Col: 15
<desc> Flange width </desc>
              ^
Expecting: '</desc>'

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

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

最终结果是 endp 解析器失败了,但是它 改变了状态 (我们已经过了 space) .您希望解析器失败并且 更改状态(在 space 之前)。 manyTill (which are referred to by manyCharsTill) 的文档解释了这一点:

The parser manyTill p endp repeatedly applies the parser p for as long as endp fails (without changing the parser state).

您可以使用 .>>.? operator:

The parser p1 .>>.? p2 behaves like p1 .>>. p2, except that it will backtrack to the beginning if p2 fails with a non‐fatal error and without changing the parser state, even if p1 has changed the parser state.

因此改为:

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

有关工作演示,请参阅 this fiddle