2012-10-08 59 views
2

我正在爲簡單的空白敏感語言編寫一個漂亮的打印機。我喜歡Leijen漂亮打印機庫比我更喜歡Wadler庫,但是Leijen庫在我的域中有一個問題:我插入的任何換行符可能會被group構造覆蓋,這可能會壓縮任何行,這可能會改變輸出的語義。使用wl-pprint的不可分行換行

我不認爲我可以在wl-pprint中實現一個不可分割的行(雖然我很想錯)。

看了一下wl-pprint-extras軟件包,我不認爲連公開的內部接口都不允許我創建一個不會被group壓扁的行。

我只需要依靠我從不使用group的事實,還是我有更好的選擇?

回答

1

鑑於您希望能夠進行分組,並且您還需要能夠確保某些行不會被解除, 爲什麼我們不使用庫設計器對數據類型中的語義進行編碼這一事實, 而不是在代碼中。這個神話般的決定使其顯着可重新設計。

Doc數據類型使用構造函數Line :: Bool -> Doc編碼換行符。 布爾表示刪除線條時是否省略空格。 (行縮進時,他們在那裏。) 讓我們更換布爾:

data LineBehaviour = OmitSpace | AddSpace | Keep 

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

關於語義作爲數據設計美麗的事情是,如果我們更換 這個Bool數據與LineBehaviour數據,即沒有使用它,但 把它傳給了功能不變不需要編輯。看看Bool與012xx什麼時候發生變化的函數 - 我們將通過更改舊語義所在的數據類型來完全重寫代碼 中需要更改以支持新語義的部分。在我們完成所有 更改之前,程序將不會編譯,而我們不需要觸及不依賴換行符語義的 的一行代碼。萬歲!

例如,renderPretty使用Line構造,但在模式Line _, 所以我們可以把單獨。

首先,我們需要更換Line TrueLine OmitSpace,並Line FalseLine 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 

就是這樣:我無法找到任何東西來改變!

+0

美麗。謝謝。當我需要這樣一個更大/更重要的項目時,我會看看這裏。 –

1

你確實需要避免group,是的。該庫旨在根據您指定的輸出寬度簡化包裝或不包裝。

您所實現語言的語法依賴,你也應謹慎看待softlinesoftbreak</><//>使用它們的運營商。沒有理由我可以看到你不能使用<$><$$>

sepfillSepcatfillCat所有使用group直接或間接地(並具有不確定的語義/依賴寬度換行符要避免的)。不過,考慮到你的目的,我不認爲你需要他們:

使用vsephsep代替sepfillSep
使用hcatvcat而不是catfillCat

你可以使用如下一行

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

,以確保您不會調用這些函數。

我想不出一種方法來確保你使用的函數不會在組件的某個地方調用組,但我認爲這些是應該避免的。

+0

我猜如果我足夠關心,我可以用不同的基礎數據結構重寫庫,這個結構具有不可分的行構造函數。它不應該改變算法,應該嗎? 恰恰相反,大多數這些組合器對我的語言來說都沒問題。我需要的唯一不變量是兩個語句從不分組。 –

+0

比重寫更容易:複製課程,註釋掉'group',註釋掉我提到並編譯的其他函數。在每次編譯器錯誤後,將錯誤的函數註釋掉。這樣你就可以得到一個你自己的無組織庫。將其稱爲'Text.PrettyPrint.LeijenHardLine'並將其安裝在本地,或者如果您的項目較小,則將其保存在與其他代碼相同的文件夾中。 – AndrewC

+0

但是我傾向於過度工程,並且有些地方我希望能夠分組,還有其他我想確保沒有發生分組的地方。我的實際解決方案只是要小心。 –