stream.UserState 更新时 FParsec ‘many’ 原语失败

FParsec ‘many’ primitive fails when stream.UserState updated

以下例程是 official documentation, "Tracing a parser"“解析器跟踪包装器的一个小的简化更改。

let (<!>) (parser: Parser<_,USER_STATE>) label : Parser<_,USER_STATE> =
    fun stream ->
        do stream.UserState <- stream.UserState  // <= works if commented out!
        let reply = parser stream
        reply

此包装器允许在解析器执行时检查 reply 并根据需要更新 stream.UserState

注意:此代码只是将 stream.UserState 复制到自身,实际上什么都不做,因为这是出现以下错误的最小操作。 official documentation manipulating stream.UserState ("Recursive grammers with nesting restrictions”) 操纵 stream.UserState 更多...

在第 3 行注释掉 do stream.UserState <- stream.UserState 允许重复(生成列表)和非重复 FParsec 原语成功.对于这些生成列表的原语,最后的从属解析器失败被解除并且应用程序,例如,many 成功。

如果上述包装器中的 parser 不是 创建结果列表的 FParsec 原语(如 manysepEndBy) , 从属解析器的重复应用则这段代码解析成功.

如果parser一个FParsec重复原语(例如manysepEndBy),那么从属解析器应用程序的失败是作为重复 FParsec 原语的失败也传回 - 意外失败.

为什么包含 do stream.UserState <- stream.UserState 会导致像 many 这样的 FParsec 原语失败?

EDIT1:请注意,FParsec documentationstream.UserState 进行了分配,如本问题中所做的那样。 @brianbern,根据文档,我不明白你的 post 我在做什么是错的。谢谢!

如何在不中断对 FParsec many 原语的调用的情况下分配给 stream.UserState

发生这种情况的原因是 CharStream.UserState 的 setter 作为副作用增加了流的 StateTag。来自 FParsec source code:

public TUserState UserState {
    get { return _UserState; }
    set { _UserState = value; ++StateTag; }
}

因此,当您将流的 UserState 分配给自身时, 等同于什么都不做。

更新:“many”解析器的文档指出:

The parser many p repeatedly applies the parser p until p fails. It returns a list of the results returned by p. At the end of the sequence p must fail without changing the parser state and without signalling a FatalError, otherwise many p will fail with the error reported by p.

FParsec 文档中的示例不会在序列末尾修改解析器状态,但您的示例解析器会在每次调用时修改解析器状态。