-- | Pretty-printing {-# LANGUAGE PatternGuards, QuasiQuotes, TypeSynonymInstances #-} module Ppr ( pprTyApp, -- * Re-exports module PprClass, module Prec ) where import Meta.Quasi import PprClass 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 Data.List (intersperse) instance IsInfix (Type i) where isInfix [$ty| ($_, $_) $lid:n |] = isOperator n isInfix [$ty| $_ -[$_]> $_ |] = True isInfix _ = False -- | For printing infix expressions. Given a splitter function that -- splits expressions into a left operand, operator name, and right -- operand (if possible), and an expression to print, pretty-prints -- the expression, but only if there is one level of infix to be -- done. pprInfix :: Ppr a => (a -> Maybe (a, String, Maybe a)) -> a -> Maybe Doc pprInfix inspect x0 | Just (x1, op, Nothing) <- inspect x0 , precOp op == Right precBang = Just (prec precBang (text op <+> ppr x1)) | Just (_, op, Just _) <- inspect x0 , isOperator (lid op :: Lid Raw) , p <- precOp op , p /= Right precBang = Just $ prec (id|||id $ p) $ fcat $ mapTail (nest 2) $ loop p empty x0 | otherwise = Nothing where loop p suf x | Just (x1, op, Just x2) <- inspect x , precOp op == p = case precOp op of Left _ -> loop p (oper op) x1 ++ [ppr1 x2 <> suf] Right _ -> ppr1 x1 <> oper op : loop p suf x2 loop _ suf x = [ ppr x <> suf ] oper s = case s of '@':_ -> text s ';':_ -> text s <> space _ -> space <> text s <> space instance Ppr (Type i) where -- pprPrec p (TyFun q t1 t2) ppr [$ty| $t1 -[$q]> $t2 |] = prec precArr $ sep [ ppr1 t1, pprArr (view q) <+> pprRight t2 ] where pprArr (QeLit Qu) = text "->" pprArr (QeLit Qa) = text "-o" pprArr _ = text "-[" <> ppr0 q <> text "]>" ppr t@[$ty| ($list:ts) $qlid:n |] | Just doc <- pprInfix unfoldType t = doc | null ts = ppr n | otherwise = prec precApp $ sep [ ppr ts, ppr n ] -- debugging: <> text (show (ttId (unsafeCoerce tag :: TyTag))) ppr [$ty| '$x |] = ppr x ppr [$ty| $quant:qu '$x. $t |] = prec precDot $ ppr qu <+> fsep (map ppr1 tvs) <> char '.' >+> ppr body where (tvs, body) = unfoldTyQu qu [$ty| $quant:qu '$x. $t |] ppr [$ty| mu '$x. $t |] = prec precDot $ text "mu" <+> ppr1 x <> char '.' >+> ppr t ppr [$ty| $anti:a |] = ppr a unfoldType :: Type i -> Maybe (Type i, String, Maybe (Type i)) unfoldType [$ty| ($t1, $t2) $name:n |] = Just (t1, n, Just t2) unfoldType [$ty| $t1 $name:n |] = Just (t1, n, Nothing) unfoldType _ = Nothing instance Ppr (TyPat i) where ppr tp0 = case tp0 of _ | Just doc <- pprInfix unfoldTyPat tp0 -> doc N _ (TpVar tv var) -> pprParamV var tv [$tpQ| ($list:tps) $qlid:ql |] -> ppr tps <+> ppr ql [$tpQ| $antiP:a |] -> ppr a unfoldTyPat :: TyPat i -> Maybe (TyPat i, String, Maybe (TyPat i)) unfoldTyPat [$tpQ| ($t1, $t2) $name:n |] = Just (t1, n, Just t2) unfoldTyPat [$tpQ| $t1 $name:n |] = Just (t1, n, Nothing) unfoldTyPat _ = Nothing instance Ppr (QExp i) where ppr [$qeQ| $qlit:qu |] = ppr qu ppr [$qeQ| $qvar:v |] = ppr (tvname v) ppr [$qeQ| $qdisj:qes |] = case qes of [] -> ppr Qu [qe] -> ppr qe _ -> prec precPlus $ fsep $ intersperse (text "\\/") $ map ppr1 qes ppr [$qeQ| $qconj:qes |] = case qes of [] -> ppr Qa [qe] -> ppr qe _ -> prec precStar $ hcat $ intersperse (text "/\\") $ map ppr1 qes ppr [$qeQ| $anti:a |] = ppr a instance Ppr (Prog i) where ppr [$prQ| $list:ms |] = vcat (map ppr0 ms) ppr [$prQ| $expr:e |] = ppr e ppr [$prQ| $list:ms in $e |] = vcat (map ppr0 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 ppr0 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 ppr0 sgs)) $$ text "end" [$seQ| $_ with type $list:_ $qlid:_ = $_ |] -> error "BUG! can't happen in pprSigExp" [$seQ| $anti:a |] -> add (ppr a) withs = atPrec 0 $ sep $ mapHead (text "with type" <+>) $ mapTail ((nest 6) . (text "and" <+>)) $ [ pprTyApp 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 ppr e0 = case e0 of _ | Just doc <- pprInfix unfoldExpr e0 -> doc [$ex| $id:x |] -> ppr x [$ex| $lit:lt |] -> ppr lt [$ex| if $ec then $et else $ef |] -> prec precDot $ sep [ text "if" <+> ppr ec, nest 2 $ text "then" <+> ppr0 et, nest 2 $ text "else" <+> ppr ef ] [$ex| $_; $_ |] -> prec precDot $ sep (unfold e0) where unfold [$ex| $e1; $e2 |] = ppr1 e1 <> semi : unfold e2 unfold e = [ ppr0 e ] [$ex| let $x = $e1 in $e2 |] -> pprLet (ppr x) e1 e2 [$ex| match $e1 with $list:clauses |] -> prec precDot $ vcat (sep [ text "match", nest 2 $ ppr0 e1, text "with" ] : map alt clauses) where alt (N _ (CaClause xi ei)) = hang (char '|' <+> ppr xi <+> text "->") 4 (ppr ei) alt (N _ (CaAnti a)) = char '|' <+> ppr a [$ex| let rec $list:bs in $e2 |] -> prec precDot $ text "let" <+> vcat (zipWith each ("rec" : repeat "and") bs) $$ text "in" <+> ppr 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 |] -> prec precDot $ text "let" <+> ppr0 d $$ (text "in" >+> ppr e2) [$ex| ($e1, $e2) |] -> prec precCom $ sep [ ppr e1 <> comma, ppr1 e2 ] [$ex| fun $_ : $_ -> $_ |] -> pprAbs e0 [$ex| $e1 $e2 |] -> prec precApp $ sep [ ppr e1, ppr1 e2 ] [$ex| fun '$_ -> $_ |] -> pprAbs e0 [$ex| $_ [$_] |] -> prec precTApp $ cat [ ppr op, brackets . fsep . punctuate comma $ map (pprPrec precCom) args ] where (args, op) = unfoldExTApp e0 [$ex| Pack[$opt:t1]($t2, $e) |] -> prec precApp $ text "Pack" <> maybe empty (brackets . ppr0) t1 <+> prec precCom (sep [ ppr1 t2 <> comma, ppr e ]) [$ex| ( $e : $t1 :> $t2 ) |] -> prec precCast $ atPrec (precCast + 2) $ sep [ ppr e, colon <+> ppr t1, text ":>" <+> ppr t2 ] [$ex| ( $e : $t1 ) |] -> prec precCast $ atPrec (precCast + 2) $ sep [ ppr e, colon <+> ppr t1 ] [$ex| ( $e :> $t1 ) |] -> prec precCast $ atPrec (precCast + 2) $ sep [ ppr e, text ":>" <+> ppr t1 ] [$ex| $anti:a |] -> ppr a where unfoldExpr [$ex| ($name:x $e1) $e2 |] = Just (e1, x, Just e2) unfoldExpr [$ex| $name:x $e1 |] = Just (e1, x, Nothing) unfoldExpr _ = Nothing pprLet :: Doc -> Expr i -> Expr i -> Doc pprLet pat e1 e2 = prec precDot $ hang (text "let" <+> pat <+> pprArgList args <+> equals >+> ppr body <+> text "in") (if isLet (view e2) then 0 else 2) (ppr e2) where (args, body) = unfoldExAbs e1 isLet (ExCase _ [_]) = True isLet _ = False pprAbs :: Expr i -> Doc pprAbs e = prec precDot $ text "fun" <+> argsDoc <+> text "->" >+> ppr 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 $ ppr0 x >+> colon <+> ppr0 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 ppr [$pa| _ |] = text "_" ppr [$pa| $lid:l |] = ppr l ppr [$pa| $quid:qu |] = ppr qu ppr [$pa| $quid:qu $x |] = prec precApp $ ppr qu <+> ppr1 x ppr [$pa| ($x, $y) |] = prec precCom $ ppr x <> comma <+> ppr1 y ppr [$pa| $lit:lt |] = ppr lt ppr [$pa| $x as $lid:l |] = prec precDot $ ppr1 x <+> text "as" <+> ppr l ppr [$pa| Pack('$tv,$x) |] = prec precApp $ text "Pack" <+> pprPrec precCom (sep pair) where pair = [ ppr1 tv <> comma, ppr x ] ppr [$pa| $anti:a |] = ppr 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 -- -- Helper for pretty-printing type-like things -- doesn't require -- underlying types, but does need to see the operator name. -- data PprTyAppHelper i a = PTAHBranch (QLid i) [a] | PTAHLeaf a instance Ppr a => Ppr (PprTyAppHelper i a) where ppr (PTAHLeaf a) = ppr a ppr _ = error "BUG! in PprTyAppHelper.ppr" unfoldPTAH :: PprTyAppHelper i a -> Maybe (PprTyAppHelper i a, String, Maybe (PprTyAppHelper i a)) unfoldPTAH (PTAHBranch (J [] l) [a, b]) = Just (PTAHLeaf a, unLid l, Just (PTAHLeaf b)) unfoldPTAH (PTAHBranch (J [] l) [a]) = Just (PTAHLeaf a, unLid l, Nothing) unfoldPTAH _ = Nothing pprTyApp :: Ppr a => QLid i -> [a] -> Doc pprTyApp ql ts | Just doc <- pprInfix unfoldPTAH (PTAHBranch ql ts) = doc pprTyApp ql [] = ppr ql pprTyApp ql ts = prec precApp $ sep [ ppr ts, ppr ql ] -- -- Instances -- 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 Loc where pprPrec = pprFromShow 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