4

我正在使用带有自定义Stream类型的 Parsec。这个流本质上是String但有时它将在字符串中找到的输入扩展为其他字符串(想想别名扩展)。例如,给定“§4.1 ¶3”,它可能会向解析器提供“第 4.1 节第 3 段”。

我有这一切工作。我的类型看起来像:

data DealiasingStream = ...
instance (Monad m) => Stream DealiasingStream m Char where ...

type ShellParser = Parsec DealiasingStream ()

请注意,依赖类型DealiasingStream是 just Char。这允许我的解析器(好吧,我ShellParser的 s 使用所有标准字符解析器。

我的问题是关于让 Parsec 根据我的流的原始输入报告位置。的文档Stream说:

一个Stream实例负责在流状态中维护“流中的位置” s。除非您以非平凡的方式使用 monad,否则这是微不足道的。

事实上,我的流类型知道它想在任何给定时刻报告什么位置......但我不知道如何让 Parsec 使用它!Parsec 似乎将其自身SourcePos作为其内部State. 这似乎是由各种tokenprim 更新的,因此对于标准Char解析器来说,这是我无法控制的。

一个人应该如何做到这一点?

4

2 回答 2

1

我同意你的理解——如果不重写诸如char.

文档的意思是Stream实例负责记录令牌内的位置信息。然后,该信息可以用于类似tokenor的函数tokenPrim(通过向它们提供适当的位置计算函数)。

因此,您必须包装Char到包含位置信息的数据类型中,并使用类似于位置计算的token或灵活的原语重写基本函数。tokenPrim

于 2012-11-21T22:43:55.657 回答
-1

SourcePos您可以使用 in 中的函数创建一个新的Text.Parsec.Pos,并使用 in 将其设置到解析器setPositionText.Parsec.Prim

编辑:

我不确定您为什么需要自定义流,因为您没有更改令牌类型。您应该能够使用标准解析器,并在您的规则Char中执行扩展和位置更新。whitespace我已经使用cpp扩展宏来完成此操作,并使用自定义whitespace规则来查找命令#line,我使用它来更新位置setPosition。您可以使用它来查找扩展,并通过将扩展添加到 来修改输入流getInput,然后使用 . 将结果设置回解析器setInput。的文档setInput建议将其用于扩展#include指令,这本质上是相同的问题。

于 2012-11-21T17:20:16.823 回答