2

我正在为一种简单的空白敏感语言编写漂亮的打印机。

比起 Wadler 库,我更喜欢 Leijen 漂亮的打印机库,但是 Leijen 库在我的领域有一个问题:我插入的任何换行符都可能被group构造覆盖,这可能会压缩任何行,这可能会改变语义的输出。

我认为我不能在 wl-pprint 中实现不可分组的行(尽管我很想错)。

稍微看一下 wl-pprint-extras 包,我认为即使是暴露的内部接口也不允许我创建一条不会被group.

我只需要依靠我从未使用过的事实group,还是我有更好的选择?

4

2 回答 2

1

你确实需要避免group,是的。该库旨在根据您指定的输出宽度促进包装或不包装。

根据您正在实现的语言的语法,您还应该小心使用它们的softlineandsoftbreak</>and运算符。<//>我没有理由看到你不能使用<$>and<$$>代替。

sep, fillSep,catfillCat全部group直接或间接使用(并且具有您想要避免的不确定的语义/与宽度相关的换行符)。但是,鉴于您的目的,我认为您不需要它们:

使用vseporhsep代替sepor fillSep
使用hcatorvcat代替cator fillCat

你可以使用像

import Text.PrettyPrint.Leijen hiding (group,softline,softbreak,
                                      (</>),(<//>),
                                      sep,fillSep,cat,fillCat)

确保您不调用这些函数。

我想不出一种方法来确保您使用的函数不会在某个地方调用 group ,但我认为这些是要避免的。

于 2012-10-09T08:00:07.870 回答
1

鉴于您希望能够进行分组,并且还需要能够确保某些行没有被插入,我们为什么不使用库设计者在数据类型中而不是在代码中编码语义这一事实。这个绝妙的决定使其非常可重新设计。

数据类型使用构造函数对Doc换行符进行编码Line :: Bool -> Doc。Bool 表示删除行时是否省略空格。(当它们在那里时,行缩进。)让我们替换 Bool:

data LineBehaviour = OmitSpace | AddSpace | Keep

data Doc = ...
    ...
    Line !LineBehaviour   -- not Bool any more

语义即数据设计的美妙之处在于,如果我们将这些Bool数据替换为LineBehaviour数据,那么没有使用它但未更改地传递它的函数就不需要编辑。函数查看 Bool 因更改而中断的内容 - 我们将通过更改旧语义所在的数据类型来准确重写需要更改以支持新语义的代码部分。在我们完成所有我们应该做的更改之前,程序不会编译,而我们不需要接触不依赖于换行语义的代码行。万岁!

例如,renderPretty使用Line构造函数,但在模式Line _中,所以我们可以不理会它。

首先,我们需要Line TrueLine OmitSpaceLine False替换Line AddSpace

line = Line AddSpace

linebreak = Line OmitSpace

但也许我们应该添加我们自己的

hardline :: Doc
hardline = Line Keep

我们也许可以使用使用它的二元运算符

infixr 5 <->
(<->) :: Doc -> Doc -> Doc
x <-> y = x <> hardline <> y

和垂直分隔符的等价物,我想不出比非常垂直的分隔符更好的名字:

vvsep,vvcat :: [Doc] -> Doc
vvsep = fold (<->)
vvcat = fold (<->)

行的实际删除发生在group函数中。一切都可以保持不变,除了:

flatten (Line break)    = if break then Empty else Text 1 " "

应该改为

flatten (Line OmitSpace)    = Empty
flatten (Line AddSpace)     = Text 1 " "
flatten (Line Keep)         = Line Keep

就是这样:我找不到其他可以改变的地方!

于 2012-10-09T23:45:26.070 回答