我正在使用自定义Stream
类型的Parsec。这个流基本上是String
,但有时它会将它在字符串中找到的输入扩展为其他字符串(想想别名扩展)。例如,给定“§4.1¶3”它可能会将“4.1节第3段”提供给解析器。
我把这一切都搞定了。我的类型看起来像:
data DealiasingStream = ...
instance (Monad m) => Stream DealiasingStream m Char where ...
type ShellParser = Parsec DealiasingStream ()
请注意DealiasingStream
的依赖类型只是Char
。这允许我的解析器(我的ShellParser
使用所有标准字符解析器。
我的问题是让Parsec根据我的信息流的原始输入报告位置。 Stream
的文档说:
Stream
实例负责维护流状态s
中的“流中的位置”。除非你以非平凡的方式使用monad,否则这是微不足道的。
事实上,我的流类型知道它想在任何特定时刻报告的位置......但我不知道如何让Parsec使用它! Parsec似乎维持自己的SourcePos
作为其内部State
的一部分。这似乎是由各种token
prims更新的,因此对于标准Char
解析器来说,这是我无法控制的。
一个人应该如何做到这一点?
答案 0 :(得分:1)
我同意你的理解 - 如果不重写char
等功能,就没有简单的方法来控制位置。
文档的含义是Stream
实例负责记录里面的位置信息。然后,该信息可用于token
或tokenPrim
等函数(通过向它们提供适当的位置计算函数)。
因此,您必须将Char
包装到包含位置信息的数据类型中,并使用对位置计算灵活的基元(如token
或tokenPrim
)重写基本函数。 / p>
答案 1 :(得分:-1)
您可以使用SourcePos
中的功能创建新的Text.Parsec.Pos
,并将其设置为setPosition
中Text.Parsec.Prim
的解析器。
编辑:
我不确定您为什么需要自定义流,因为您没有更改令牌类型。您应该能够使用标准的Char
解析器,并在whitespace
规则中执行扩展和定位更新。我使用cpp
来扩展宏,并使用自定义whitespace
规则来查找#line
命令,我使用setPosition
来更新位置。您可以使用它来查找扩展,并通过将扩展前置到getInput
并使用setInput
将结果设置回解析器来修改输入流。 setInput
的文档建议将其用于扩展#include
指令,这基本上是同一个问题。