alms-0.4.9: src/Ppr.hs
-- | Pretty-printing
{-# LANGUAGE
PatternGuards,
QuasiQuotes,
TypeSynonymInstances
#-}
module Ppr (
-- * Pretty-printing class
Ppr(..),
-- * Pretty-printing combinators
parensIf, (>+>), (>?>), pprTyApp,
-- * Renderers
render, renderS, printDoc, printPpr,
-- ** Instance helpers
showFromPpr, pprFromShow,
-- * Re-exports
module Text.PrettyPrint,
module Prec
) where
import Meta.Quasi
import Prec
import Syntax
import Util
import qualified Syntax.Decl
import qualified Syntax.Expr
import qualified Syntax.Notable
import qualified Syntax.Patt
import qualified Loc
import Text.PrettyPrint hiding (render)
import Data.List (intersperse)
-- | Class for pretty-printing at different types
--
-- Minimal complete definition is one of:
--
-- * 'pprPrec'
--
-- * 'ppr'
class Ppr p where
-- | Print at the specified enclosing precedence
pprPrec :: Int -> p -> Doc
-- | Print at top-level precedence
ppr :: p -> Doc
-- | Print a list at the specified enclosing precedence with
-- the specified style
pprPrecStyleList :: Int -> ListStyle -> [p] -> Doc
-- | Print a list at the specified enclosing precedence
pprPrecList :: Int -> [p] -> Doc
-- | Print a list at top-level precedence
pprList :: [p] -> Doc
-- | Style for printing lists
listStyle :: [p] -> ListStyle
--
ppr = pprPrec precDot
pprPrec _ = ppr
pprPrecStyleList _ st [] =
if listStyleDelimitEmpty st
then listStyleBegin st <> listStyleEnd st
else empty
pprPrecStyleList p st [x] =
if listStyleDelimitSingleton st
then listStyleBegin st <> ppr x <> listStyleEnd st
else pprPrec p x
pprPrecStyleList _ st xs =
listStyleBegin st <>
listStyleJoiner st (punctuate (listStylePunct st) (map ppr xs))
<> listStyleEnd st
pprPrecList p xs = pprPrecStyleList p (listStyle xs) xs
pprList = pprPrecList 0
listStyle _ = ListStyle {
listStyleBegin = lparen,
listStyleEnd = rparen,
listStylePunct = comma,
listStyleDelimitEmpty = False,
listStyleDelimitSingleton = False,
listStyleJoiner = fsep
}
data ListStyle = ListStyle {
listStyleBegin, listStyleEnd, listStylePunct :: Doc,
listStyleDelimitEmpty, listStyleDelimitSingleton :: Bool,
listStyleJoiner :: [Doc] -> Doc
}
-- | Conditionally add parens around the given 'Doc'
parensIf :: Bool -> Doc -> Doc
parensIf True doc = parens doc
parensIf False doc = doc
instance Ppr a => Ppr [a] where
pprPrec = pprPrecList
instance Ppr a => Ppr (Maybe a) where
pprPrec _ Nothing = empty
pprPrec p (Just a) = pprPrec p a
class Ppr a => IsInfix a where
isInfix :: a -> Bool
pprRight :: Int -> a -> Doc
pprRight p a =
if isInfix a
then pprPrec p a
else ppr a
instance Ppr Doc where
ppr = id
instance IsInfix (Type i) where
isInfix [$ty| ($_, $_) $lid:n |] = isOperator n
isInfix [$ty| $_ -[$_]> $_ |] = True
isInfix _ = False
-- | To pretty print the application of a type constructor to
-- generic parameters
pprTyApp :: (Ppr a) => Int -> QLid i -> [a] -> Doc
pprTyApp _ ql [] = ppr ql
pprTyApp p (J [] l) [t1]
| isOperator l, precOp (unLid l) == Right precBang
= parensIf (p > precBang) $
text (unLid l) <> pprPrec (precBang + 1) t1
pprTyApp p (J [] l) [t1, t2]
-- print @ without space around it:
| isOperator l, '@':_ <- unLid l, Right prec <- precOp (unLid l)
= parensIf (p > prec) $
pprPrec (prec + 1) t1 <> text (unLid l) <> pprPrec prec t2
| isOperator l, Left prec <- precOp (unLid l)
= parensIf (p > prec) $
sep [ pprPrec prec t1,
text (unLid l) <+> pprPrec (prec + 1) t2 ]
| isOperator l, Right prec <- precOp (unLid l)
= parensIf (p > prec) $
sep [ pprPrec (prec + 1) t1,
text (unLid l) <+> pprPrec prec t2]
pprTyApp p ql ts = parensIf (p > precApp) $
sep [ pprPrec precApp ts,
ppr ql ]
instance Ppr (Type i) where
-- Print sugar for infix type constructors:
pprPrec p [$ty| $t1 ; $t2 |]
= parensIf (p > precSemi) $
sep [ pprPrec (precSemi + 1) t1 <> text ";",
pprPrec precSemi t2 ]
-- pprPrec p (TyFun q t1 t2)
pprPrec p [$ty| $t1 -[$q]> $t2 |]
= parensIf (p > precArr) $
sep [ pprPrec (precArr + 1) t1,
pprArr (view q) <+> pprRight precArr t2 ]
where pprArr (QeLit Qu) = text "->"
pprArr (QeLit Qa) = text "-o"
pprArr _ = text "-[" <> pprPrec precStart q <> text "]>"
pprPrec p [$ty| ($list:ts) $qlid:n |]
= pprTyApp p n ts
-- debugging: <> text (show (ttId (unsafeCoerce tag :: TyTag)))
pprPrec p [$ty| '$x |] = pprPrec p x
pprPrec p [$ty| $quant:qu '$x. $t |]
= parensIf (p > precDot) $
ppr qu <+>
fsep (map (pprPrec (precDot + 1))
tvs) <>
char '.'
>+> pprPrec precDot body
where (tvs, body) = unfoldTyQu qu [$ty| $quant:qu '$x. $t |]
pprPrec p [$ty| mu '$x. $t |]
= parensIf (p > precDot) $
text "mu" <+>
pprPrec (precDot + 1) x <>
char '.'
>+> pprPrec precDot t
pprPrec p [$ty| $anti:a |] = pprPrec p a
instance Ppr (TyPat i) where
pprPrec p tp0 = case tp0 of
N _ (TpVar tv var) -> pprParamV var tv
[$tpQ| ($list:tps) $qlid:ql |]
-> pprTyApp p ql tps
[$tpQ| $antiP:a |] -> ppr a
instance Ppr (QExp i) where
pprPrec p [$qeQ| $qlit:qu |] = pprPrec p qu
pprPrec p [$qeQ| $qvar:v |] = pprPrec p (tvname v)
pprPrec p [$qeQ| $qdisj:qes |] = case qes of
[] -> pprPrec p Qu
[qe] -> pprPrec p qe
_ -> parensIf (p > precPlus) $
fsep $
intersperse (text "\\/") $
map (pprPrec (precPlus + 1)) qes
pprPrec p [$qeQ| $qconj:qes |] = case qes of
[] -> pprPrec p Qa
[qe] -> pprPrec p qe
_ -> parensIf (p > precStar) $
hcat $
intersperse (text "/\\") $
map (pprPrec (precStar + 1)) qes
pprPrec p [$qeQ| $anti:a |] = pprPrec p a
instance Ppr Int where
ppr = int
instance Ppr (Prog i) where
ppr [$prQ| $list:ms |] = vcat (map ppr ms)
ppr [$prQ| $expr:e |] = ppr e
ppr [$prQ| $list:ms in $e |] = vcat (map ppr ms) $+$
(text "in" >+> ppr e)
instance Ppr (Decl i) where
ppr [$dc| let $x = $e |] = sep
[ text "let" <+> ppr x,
nest 2 $ equals <+> ppr e ]
ppr [$dc| let $x : $t = $e |] = sep
[ text "let" <+> ppr x,
nest 2 $ colon <+> ppr t,
nest 4 $ equals <+> ppr e ]
ppr [$dc| type $list:tds |] = pprTyDecs tds
ppr [$dc| abstype $list:ats0 with $list:ds end |] =
case ats0 of
[] ->
vcat [
text "abstype with",
nest 2 $ vcat (map ppr ds),
text "end"
]
at:ats ->
vcat [
vcat (text "abstype" <+> pprAbsTy at :
[ nest 4 $ text "and" <+> pprAbsTy ati | ati <- ats ])
<+> text "with",
nest 2 $ vcat (map ppr ds),
text "end"
]
ppr [$dc| open $b |] = pprModExp (text "open" <+>) b
ppr [$dc| module $uid:n : $s = $b |] = pprModExp add1 b where
add1 body = pprSigExp add2 s <+> equals <+> body
add2 body = text "module" <+> ppr n <+> colon <+> body
ppr [$dc| module $uid:n = $b |] = pprModExp add b where
add body = text "module" <+> ppr n <+> equals <+> body
ppr [$dc| module type $uid:n = $s |] = pprSigExp add s where
add body = text "module type" <+> ppr n <+> equals <+> body
ppr [$dc| local $list:d0 with $list:d1 end |] =
vcat [
text "local",
nest 2 (vcat (map ppr d0)),
text "with",
nest 2 (vcat (map ppr d1)),
text "end"
]
ppr [$dc| exception $uid:n of $opt:mt |] =
pprExcDec n mt
ppr [$dc| $anti:a |] = ppr a
pprTyDecs :: [TyDec i] -> Doc
pprTyDecs tds =
vcat $
mapHead (text "type" <+>) $
mapTail ((nest 1) . (text "and" <+>)) $
map ppr tds
pprExcDec :: Uid i -> Maybe (Type i) -> Doc
pprExcDec u Nothing =
text "exception" <+> ppr u
pprExcDec u (Just t) =
text "exception" <+> ppr u <+> text "of" <+> ppr t
instance Ppr (TyDec i) where
ppr td = case view td of
TdAbs n ps vs qs -> pprProtoV n vs ps >?> pprQuals qs
TdSyn n [(ps,t)] -> pprProto n ps >+> equals <+> ppr t
TdSyn n cs -> vcat [ char '|' <+> each ci | ci <- cs ]
where
each (ps, rhs) = pprProto n ps
>+> char '=' <+> ppr rhs
TdDat n ps alts -> pprProtoV n (repeat Invariant) ps
>?> pprAlternatives alts
TdAnti a -> ppr a
pprAbsTy :: AbsTy i -> Doc
pprAbsTy at = case view at of
AbsTy variances qual (N _ (TdDat name params alts)) ->
pprProtoV name variances params
>?> pprQuals qual
>?> pprAlternatives alts
AbsTy _ _ td -> ppr td -- shouldn't happen (yet)
AbsTyAnti a -> ppr a
pprProto :: Lid i -> [TyPat i] -> Doc
pprProto n ps = ppr (tpApp (J [] n) ps)
pprProtoV :: Lid i -> [Variance] -> [TyVar i] -> Doc
pprProtoV n vs tvs = pprProto n (zipWith tpVar tvs vs)
pprParamV :: Variance -> TyVar i -> Doc
pprParamV Invariant tv = ppr tv
pprParamV v tv = ppr v <> ppr tv
pprQuals :: QExp i -> Doc
pprQuals [$qeQ| U |] = empty
pprQuals qs = text ":" <+> pprPrec precApp qs
pprAlternatives :: [(Uid i, Maybe (Type i))] -> Doc
pprAlternatives [] = equals
pprAlternatives (a:as) = sep $
equals <+> alt a : [ char '|' <+> alt a' | a' <- as ]
where
alt (u, Nothing) = ppr u
alt (u, Just t) = ppr u <+> text "of" <+> pprPrec precDot t
pprModExp :: (Doc -> Doc) -> ModExp i -> Doc
pprModExp add modexp = case modexp of
[$me| $quid:n |] -> add (ppr n)
[$me| $quid:n $list:qls |] ->
add (ppr n) <+>
brackets (fsep (punctuate comma (map ppr qls)))
[$me| struct $list:ds end |] ->
add (text "struct")
$$ nest 2 (vcat (map ppr ds))
$$ text "end"
[$me| $me1 : $se2 |] ->
pprSigExp (pprModExp add me1 <+> colon <+>) se2
[$me| $anti:a |] -> add (ppr a)
pprSigExp :: (Doc -> Doc) -> SigExp i -> Doc
pprSigExp add se0 = body >+> withs where
(wts, se1) = unfoldSeWith se0
body = case se1 of
[$seQ| $quid:n |] -> add (ppr n)
[$seQ| $quid:n $list:qls |] ->
add (ppr n) <+>
brackets (fsep (punctuate comma (map ppr qls)))
[$seQ| sig $list:sgs end |] ->
add (text "sig")
$$ nest 2 (vcat (map ppr sgs))
$$ text "end"
[$seQ| $_ with type $list:_ $qlid:_ = $_ |] ->
error "BUG! can't happen in pprSigExp"
[$seQ| $anti:a |] -> add (ppr a)
withs =
sep $
mapHead (text "with type" <+>) $
mapTail ((nest 6) . (text "and" <+>)) $
[ pprTyApp 0 tc tvs <+> equals <+> ppr t
| (tc, tvs, t) <- wts ]
instance Ppr (SigItem i) where
ppr sg0 = case sg0 of
[$sgQ| val $lid:n : $t |] ->
text "val" <+> ppr n >+> colon <+> ppr t
[$sgQ| type $list:tds |] ->
pprTyDecs tds
[$sgQ| module $uid:u : $s |] ->
pprSigExp add s where
add body = text "module" <+> ppr u <+> colon <+> body
[$sgQ| module type $uid:u = $s |] ->
pprSigExp add s where
add body = text "module type" <+> ppr u <+> equals <+> body
[$sgQ| include $s |] ->
pprSigExp (text "include" <+>) s
[$sgQ| exception $uid:u of $opt:mt |] ->
pprExcDec u mt
[$sgQ| $anti:a |] ->
ppr a
instance Ppr (Expr i) where
pprPrec p e0 = case e0 of
[$ex| $id:x |] -> pprPrec p x
[$ex| $lit:lt |] -> pprPrec p lt
[$ex| if $ec then $et else $ef |] ->
parensIf (p > precDot) $
sep [ text "if" <+> ppr ec,
nest 2 $ text "then" <+> ppr et,
nest 2 $ text "else" <+> pprPrec precDot ef ]
[$ex| $e1; $e2 |] ->
parensIf (p > precSemi) $
sep [ pprPrec (precSemi + 1) e1 <> semi,
ppr e2 ]
[$ex| let $x = $e1 in $e2 |] ->
pprLet p (ppr x) e1 e2
[$ex| match $e1 with $list:clauses |] ->
parensIf (p > precDot) $
vcat (sep [ text "match",
nest 2 $ ppr e1,
text "with" ] : map alt clauses)
where
alt (N _ (CaClause xi ei)) =
hang (char '|' <+> pprPrec precDot xi <+> text "->")
4
(pprPrec precDot ei)
alt (N _ (CaAnti a)) = char '|' <+> ppr a
[$ex| let rec $list:bs in $e2 |] ->
text "let" <+>
vcat (zipWith each ("rec" : repeat "and") bs) $$
text "in" <+> pprPrec precDot e2
where
each kw (N _ (BnBind x t e)) =
-- This could be better by pulling some args out.
hang (hang (text kw <+> ppr x)
6
(colon <+> ppr t <+> equals))
2
(ppr e)
each kw (N _ (BnAnti a)) = text kw <+> ppr a
[$ex| let $decl:d in $e2 |] ->
text "let" <+> ppr d $$
(text "in" >+> pprPrec precDot e2)
[$ex| ($e1, $e2) |] ->
parensIf (p > precCom) $
sep [ pprPrec precCom e1 <> comma,
pprPrec (precCom + 1) e2 ]
[$ex| fun $_ : $_ -> $_ |] -> pprAbs p e0
[$ex| $name:x $e2 |]
| Right p' <- precOp x,
p' == 10
-> parensIf (p > p') $
text x <+> pprPrec p' e2
[$ex| ($name:x $e12) $e2 |]
| (dl, dr, p') <- either ((,,) 0 1) ((,,) 1 0) (precOp x),
p' < 9
-> parensIf (p > p') $
sep [ pprPrec (p' + dl) e12,
text x,
pprPrec (p' + dr) e2 ]
[$ex| $e1 $e2 |]
-> parensIf (p > precApp) $
sep [ pprPrec precApp e1,
pprPrec (precApp + 1) e2 ]
[$ex| fun '$_ -> $_ |] -> pprAbs p e0
[$ex| $_ [$_] |] ->
parensIf (p > precTApp) $
cat [ pprPrec precTApp op,
brackets . fsep . punctuate comma $
map (pprPrec precCom) args ]
where
(args, op) = unfoldExTApp e0
[$ex| Pack[$opt:t1]($t2, $e) |] ->
parensIf (p > precApp) $
text "Pack" <> maybe empty (brackets . ppr) t1 <+>
parens (sep [ pprPrec (precCom + 1) t2 <> comma,
pprPrec precCom e ])
[$ex| ( $e : $t1 :> $t2 ) |] ->
parensIf (p > precCast) $
sep [ pprPrec (precCast + 2) e,
colon <+> pprPrec (precCast + 2) t1,
text ":>" <+> pprPrec (precCast + 2) t2 ]
[$ex| ( $e : $t1 ) |] ->
parensIf (p > precCast) $
sep [ pprPrec (precCast + 2) e,
colon <+> pprPrec (precCast + 2) t1 ]
[$ex| ( $e :> $t1 ) |] ->
parensIf (p > precCast) $
sep [ pprPrec (precCast + 2) e,
text ":>" <+> pprPrec (precCast + 2) t1 ]
[$ex| $anti:a |] -> pprPrec p a
pprLet :: Int -> Doc -> Expr i -> Expr i -> Doc
pprLet p pat e1 e2 = parensIf (p > precDot) $
hang (text "let" <+> pat <+> pprArgList args <+> equals
>+> ppr body <+> text "in")
(if isLet (view e2)
then 0
else 2)
(pprPrec precDot e2)
where
(args, body) = unfoldExAbs e1
isLet (ExCase _ [_]) = True
isLet _ = False
pprAbs :: Int -> Expr i -> Doc
pprAbs p e = parensIf (p > precDot) $
text "fun" <+> argsDoc <+> text "->"
>+> pprPrec precDot body
where (args, body) = unfoldExAbs e
argsDoc = case args of
[Left ([$pa| _ |], [$ty|@! unit |])]
-> parens empty
[Left (x, t)] -> ppr x <+> char ':' <+> pprPrec (precArr + 1) t
_ -> pprArgList args
pprArgList :: [Either (Patt i, Type i) (TyVar i)] -> Doc
pprArgList = fsep . map eachArg . combine where
eachArg (Left ([$pa| _ |], [$ty|@! unit |]))
= parens empty
eachArg (Left (x, t)) = parens $
ppr x
>+> colon <+> ppr t
eachArg (Right tvs) = brackets .
sep .
punctuate comma $
map ppr tvs
combine :: [Either a b] -> [Either a [b]]
combine = foldr each [] where
each (Right b) (Right bs : es) = Right (b : bs) : es
each (Right b) es = Right [b] : es
each (Left a) es = Left a : es
instance Ppr (Patt i) where
pprPrec _ [$pa| _ |] = text "_"
pprPrec _ [$pa| $lid:l |] = ppr l
pprPrec _ [$pa| $quid:qu |] = ppr qu
pprPrec p [$pa| $quid:qu $x |] = parensIf (p > precApp) $
pprPrec precApp qu <+>
pprPrec (precApp + 1) x
pprPrec p [$pa| ($x, $y) |] = parensIf (p > precCom) $
pprPrec precCom x <> comma <+>
pprPrec (precCom + 1) y
pprPrec p [$pa| $lit:lt |] = pprPrec p lt
pprPrec p [$pa| $x as $lid:l |] = parensIf (p > precDot) $
pprPrec (precDot + 1) x <+>
text "as" <+> ppr l
pprPrec p [$pa| Pack('$tv,$x) |] = parensIf (p > precApp) $
text "Pack" <+> parens (sep pair)
where pair = [ pprPrec (precCom + 1) tv <> comma,
pprPrec precCom x ]
pprPrec p [$pa| $anti:a |] = pprPrec p a
instance Ppr Lit where
ppr (LtInt i) = integer i
ppr (LtFloat f) = double f
ppr (LtStr s) = text (show s)
ppr (LtAnti a) = ppr a
instance Show (Prog i) where showsPrec = showFromPpr
instance Show (Decl i) where showsPrec = showFromPpr
instance Show (TyDec i) where showsPrec = showFromPpr
instance Show (Expr i) where showsPrec = showFromPpr
instance Show (Patt i) where showsPrec = showFromPpr
instance Show Lit where showsPrec = showFromPpr
instance Show (Type i) where showsPrec = showFromPpr
instance Show (TyPat i) where showsPrec = showFromPpr
instance Show (QExp i) where showsPrec = showFromPpr
instance Show (SigItem i)where showsPrec = showFromPpr
instance Ppr QLit where pprPrec = pprFromShow
instance Ppr Variance where pprPrec = pprFromShow
instance Ppr Quant where pprPrec = pprFromShow
instance Ppr (Lid i) where pprPrec = pprFromShow
instance Ppr (Uid i) where pprPrec = pprFromShow
instance Ppr (BIdent i)where pprPrec = pprFromShow
instance Ppr (TyVar i) where pprPrec = pprFromShow
instance Ppr Anti where pprPrec = pprFromShow
instance (Show p, Show k) => Ppr (Path p k) where pprPrec = pprFromShow
-- Render a document in the preferred style, given a string continuation
renderS :: Doc -> ShowS
renderS doc rest = fullRender PageMode 80 1.5 each rest doc
where each (Chr c) s' = c:s'
each (Str s) s' = s++s'
each (PStr s) s' = s++s'
-- Render a document in the preferred style
render :: Doc -> String
render doc = renderS doc ""
-- Render and display a document in the preferred style
printDoc :: Doc -> IO ()
printDoc = fullRender PageMode 80 1.5 each (putChar '\n')
where each (Chr c) io = putChar c >> io
each (Str s) io = putStr s >> io
each (PStr s) io = putStr s >> io
-- Pretty-print, render and display in the preferred style
printPpr :: Ppr a => a -> IO ()
printPpr = printDoc . ppr
showFromPpr :: Ppr a => Int -> a -> ShowS
showFromPpr p t = renderS (pprPrec p t)
pprFromShow :: Show a => Int -> a -> Doc
pprFromShow p t = text (showsPrec p t "")
liftEmpty :: (Doc -> Doc -> Doc) -> Doc -> Doc -> Doc
liftEmpty joiner d1 d2
| isEmpty d1 = d2
| isEmpty d2 = d1
| otherwise = joiner d1 d2
(>+>) :: Doc -> Doc -> Doc
(>+>) = flip hang 2
(>?>) :: Doc -> Doc -> Doc
(>?>) = liftEmpty (>+>)
infixr 5 >+>, >?>