1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
|
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE NoMonomorphismRestriction #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE PatternSynonyms #-}
{-# LANGUAGE ViewPatterns #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE DeriveFunctor #-}
{-# LANGUAGE DeriveTraversable #-}
{-# LANGUAGE DeriveFoldable #-}
module LambdaCube.Compiler.Pretty
( module LambdaCube.Compiler.Pretty
) where
import Data.Monoid
import Data.String
--import qualified Data.Set as Set
--import qualified Data.Map as Map
import Control.Monad.Reader
import Control.Monad.State
--import Control.Arrow hiding ((<+>))
--import Debug.Trace
import qualified Text.PrettyPrint.ANSI.Leijen as P
import LambdaCube.Compiler.Utils
-------------------------------------------------------------------------------- inherited doc operations
-- add wl-pprint combinators as necessary here
data DocOp a
= DOColor Color a
| DOHSep a a
| DOHCat a a
| DOSoftSep a a
| DOVCat a a
| DONest Int a
| DOTupled [a]
deriving (Eq, Functor, Foldable, Traversable)
data Color = Green | Blue | Underlined
deriving (Eq)
interpretDocOp :: DocOp P.Doc -> P.Doc
interpretDocOp = \case
DOHSep a b -> a P.<+> b
DOHCat a b -> a <> b
DOSoftSep a b -> a P.</> b
DOVCat a b -> a P.<$$> b
DONest n a -> P.nest n a
DOTupled a -> P.tupled a
DOColor c x -> case c of
Green -> P.dullgreen x
Blue -> P.dullblue x
Underlined -> P.underline x
-------------------------------------------------------------------------------- fixity
data Fixity
= Infix !Int
| InfixL !Int
| InfixR !Int
deriving (Eq, Show)
precedence, leftPrecedence, rightPrecedence :: Fixity -> Int
precedence = \case
Infix i -> i
InfixR i -> i
InfixL i -> i
leftPrecedence (InfixL i) = i
leftPrecedence f = precedence f + 1
rightPrecedence (InfixR i) = i
rightPrecedence f = precedence f + 1
-------------------------------------------------------------------------------- doc data type
data Doc
= DDoc (DocOp Doc)
| DAtom DocAtom
| DOp String Fixity Doc Doc
| DFreshName Bool{-used-} Doc
| DVar Int
| DUp Int Doc
deriving (Eq)
data DocAtom
= SimpleAtom String
| ComplexAtom String Int Doc DocAtom
deriving (Eq)
instance IsString Doc where
fromString = text
text = DAtom . SimpleAtom
instance Monoid Doc where
mempty = text ""
a `mappend` b = DDoc $ DOHCat a b
pattern DColor c a = DDoc (DOColor c a)
strip = \case
DColor _ x -> strip x
DUp _ x -> strip x
DFreshName _ x -> strip x
x -> x
simple x = case strip x of
DAtom{} -> True
DVar{} -> True
_ -> False
renderDoc :: Doc -> P.Doc
renderDoc = render . addPar (-10) . flip runReader [] . flip evalStateT (flip (:) <$> iterate ('\'':) "" <*> ['a'..'z']) . showVars
where
showVars x = case x of
DAtom s -> DAtom <$> showVarA s
DDoc d -> DDoc <$> traverse showVars d
DOp s pr x y -> DOp s pr <$> showVars x <*> showVars y
DVar i -> asks $ text . lookupVarName i
DFreshName True x -> gets head >>= \n -> modify tail >> local (n:) (showVars x)
DFreshName False x -> local ("_":) $ showVars x
DUp i x -> local (dropNth i) $ showVars x
where
showVarA (SimpleAtom s) = pure $ SimpleAtom s
showVarA (ComplexAtom s i d a) = ComplexAtom s i <$> showVars d <*> showVarA a
lookupVarName i xs | i < length xs = xs !! i
lookupVarName i _ = ((\s n -> n: '_': s) <$> iterate ('\'':) "" <*> ['a'..'z']) !! i
addPar :: Int -> Doc -> Doc
addPar pr x = case x of
DAtom x -> DAtom $ addParA x
DOp s pr' x y -> paren $ DOp s pr' (addPar (leftPrecedence pr') x) (addPar (rightPrecedence pr') y)
DColor c x -> DColor c $ addPar pr x
DDoc d -> DDoc $ addPar (-10) <$> d
where
addParA (SimpleAtom s) = SimpleAtom s
addParA (ComplexAtom s i d a) = ComplexAtom s i (addPar i d) $ addParA a
paren = if protect then DParen else id
where
protect = case x of
DOp _ f _ _ -> precedence f < pr
_ -> False
render x = case x of
DDoc d -> interpretDocOp $ render <$> d
DAtom x -> renderA x
DOp s _ x y -> case s of
"" -> render x P.<> render y
" " -> render x P.<+> render y
_ | simple x && simple y && s /= "," -> render x <> P.text s <> render y
| otherwise -> (render x <++> s) P.<+> render y
where
renderA (SimpleAtom s) = P.text s
renderA (ComplexAtom s _ d a) = P.text s <> render d <> renderA a
x <++> "," = x <> P.text ","
x <++> s = x P.<+> P.text s
instance Show Doc where
show = show . renderDoc
-------------------------------------------------------------------------- combinators
hsep [] = mempty
hsep xs = foldr1 (<+>) xs
vcat [] = mempty
vcat xs = foldr1 (<$$>) xs
shVar = DVar
shLet i a b = shLam' (shLet' (inBlue' $ shVar i) $ DUp i a) (DUp i b)
shLet_ a b = DFreshName True $ shLam' (shLet' (shVar 0) $ DUp 0 a) b
inGreen' = DColor Green
inBlue' = DColor Blue
epar = DColor Underlined
a <+> b = DDoc $ DOHSep a b
a </> b = DDoc $ DOSoftSep a b
a <$$> b = DDoc $ DOVCat a b
nest n = DDoc . DONest n
tupled = DDoc . DOTupled
pattern DPar l d r = DAtom (ComplexAtom l (-20) d (SimpleAtom r))
pattern DParen x = DPar "(" x ")"
pattern DBrace x = DPar "{" x "}"
pattern DSep p a b = DOp " " p a b
pattern DGlue p a b = DOp "" p a b
pattern DArr x y = DOp "->" (InfixR (-1)) x y
pattern DAnn x y = DOp ":" (InfixR (-3)) x y
pattern DApp x y = DSep (InfixL 10) x y
pattern DGlueR pr x y = DSep (InfixR pr) x y
braces = DBrace
parens = DParen
shTuple [] = "()"
shTuple [x] = DParen $ DParen x
shTuple xs = DParen $ foldr1 (DOp "," (InfixR (-20))) xs
shAnn _ True x y | strip y == "Type" = x
shAnn s _ x y = DOp s (InfixR (-3)) x y
shArr = DArr
shCstr = DOp "~" (Infix (-2))
shLet' = DOp ":=" (Infix (-4))
pattern DLam vs e = DGlueR (-10) (DAtom (ComplexAtom "\\" 11 vs (SimpleAtom " ->"))) e
hardSpace = DSep (InfixR 11)
dLam vs e = DLam (foldr1 hardSpace vs) e
shLam' x (DFreshName True d) = DFreshName True $ shLam' (DUp 0 x) d
shLam' x (DLam xs y) = DLam (hardSpace x xs) y
shLam' x y = dLam [x] y
--------------------------------------------------------------------------------
class PShow a where
pShow :: a -> Doc
ppShow = show . pShow
--------------------------------------------------------------------------------
instance PShow Bool where
pShow b = if b then "True" else "False"
instance (PShow a, PShow b) => PShow (a, b) where
pShow (a, b) = tupled [pShow a, pShow b]
instance (PShow a, PShow b, PShow c) => PShow (a, b, c) where
pShow (a, b, c) = tupled [pShow a, pShow b, pShow c]
instance PShow a => PShow [a] where
-- pShow = P.brackets . P.sep . P.punctuate P.comma . map pShow -- TODO
instance PShow a => PShow (Maybe a) where
pShow = maybe "Nothing" (("Just" `DApp`) . pShow)
--instance PShow a => PShow (Set a) where
-- pShow = pShow . Set.toList
--instance (PShow s, PShow a) => PShow (Map s a) where
-- pShow = braces . vcat . map (\(k, t) -> pShow k <> P.colon <+> pShow t) . Map.toList
instance (PShow a, PShow b) => PShow (Either a b) where
pShow = either (("Left" `DApp`) . pShow) (("Right" `DApp`) . pShow)
instance PShow Doc where
pShow x = x
instance PShow Int where pShow = fromString . show
instance PShow Integer where pShow = fromString . show
instance PShow Double where pShow = fromString . show
instance PShow Char where pShow = fromString . show
instance PShow () where pShow _ = "()"
---------------------------------------------------------------------------------
-- TODO: remove
pattern ESC a b <- (splitESC -> Just (a, b)) where ESC a b | 'm' `notElem` a = "\ESC[" ++ a ++ "m" ++ b
splitESC ('\ESC':'[': (span (/='m') -> (a, c: b))) | c == 'm' = Just (a, b)
splitESC _ = Nothing
removeEscs :: String -> String
removeEscs (ESC _ cs) = removeEscs cs
removeEscs (c: cs) = c: removeEscs cs
removeEscs [] = []
|