使用wl-pprint进行不可分组的换行符

时间:2012-10-08 21:38:05

标签: haskell pretty-print

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

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

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

看一下wl-pprint-extras包,我不认为即使暴露的内部接口也允许我创建一条不会被group压扁的行。

我是否只能依赖于我从不使用group的事实,或者我有更好的选择?

2 个答案:

答案 0 :(得分:1)

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

根据您正在实施的语言的语法,您还应该谨慎使用softlinesoftbreak以及使用它们的</><//>运算符。我无法确定您无法使用<$><$$>

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

使用vsephsep代替sepfillSep
使用hcatvcat代替catfillCat

您可以使用

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

确保不要调用这些函数。

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

答案 1 :(得分: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 True替换为Line OmitSpace,将Line 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

就是这样:我找不到任何可以改变的东西!