summaryrefslogtreecommitdiff
path: root/src/LambdaCube/Compiler/Pretty.hs
blob: 2fb116f960eea9c4da0553ca3e38380662959dc8 (plain)
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 [] = []