{- | Module : $Header$ Description : A pretty printer for FlatCurry Copyright : (c) 2015 Björn Peemöller License : BSD-3-clause Maintainer : bjp@informatik.uni-kiel.de Stability : experimental Portability : portable This module implements a pretty printer for FlatCurry modules. -} module Curry.FlatCurry.Pretty ( ppProg, ppHeader, ppExports, ppImport, ppTypeDecl, ppTypeExpr , ppFuncDecl, ppExpr, ppLiteral, ppOpDecl ) where import Data.Char (ord) import Curry.Base.Pretty import Curry.FlatCurry.Type -- |pretty-print a FlatCurry module ppProg :: Prog -> Doc ppProg (Prog m is ts fs os) = sepByBlankLine [ ppHeader m ts fs , vcat (map ppImport is) , vcat (map ppOpDecl os) , sepByBlankLine (map ppTypeDecl ts) , sepByBlankLine (map ppFuncDecl fs) ] -- |pretty-print the module header ppHeader :: String -> [TypeDecl] -> [FuncDecl] -> Doc ppHeader m ts fs = sep [text "module" <+> text m, ppExports ts fs, text "where"] -- |pretty-print the export list ppExports :: [TypeDecl] -> [FuncDecl] -> Doc ppExports ts fs = parens $ list (map ppTypeExport ts ++ ppFuncExports fs) -- |pretty-print a type export ppTypeExport :: TypeDecl -> Doc ppTypeExport (Type qn vis _ cs) | vis == Private = empty | all isPublicCons cs = ppPrefixOp qn <+> text "(..)" | otherwise = ppPrefixOp qn <+> parens (list (ppConsExports cs)) where isPublicCons (Cons _ _ v _) = v == Public ppTypeExport (TypeSyn qn vis _ _ ) | vis == Private = empty | otherwise = ppPrefixOp qn -- |pretty-print the export list of constructors ppConsExports :: [ConsDecl] -> [Doc] ppConsExports cs = [ ppPrefixOp qn | Cons qn _ Public _ <- cs] -- |pretty-print the export list of functions ppFuncExports :: [FuncDecl] -> [Doc] ppFuncExports fs = [ ppPrefixOp qn | Func qn _ Public _ _ <- fs] -- |pretty-print an import statement ppImport :: String -> Doc ppImport m = text "import" <+> text m -- |pretty-print a operator fixity declaration ppOpDecl :: OpDecl -> Doc ppOpDecl (Op qn fix n) = ppFixity fix <+> integer n <+> ppInfixOp qn -- |pretty-print the associativity keyword ppFixity :: Fixity -> Doc ppFixity InfixOp = text "infix" ppFixity InfixlOp = text "infixl" ppFixity InfixrOp = text "infixr" -- |pretty-print a type declaration ppTypeDecl :: TypeDecl -> Doc ppTypeDecl (Type qn _ vs cs) = text "data" <+> ppQName qn <+> hsep (map ppTVarIndex vs) $+$ ppConsDecls cs ppTypeDecl (TypeSyn qn _ vs ty) = text "type" <+> ppQName qn <+> hsep (map ppTVarIndex vs) <+> equals <+> ppTypeExpr 0 ty -- |pretty-print the constructor declarations ppConsDecls :: [ConsDecl] -> Doc ppConsDecls cs = indent $ vcat $ zipWith (<+>) (equals : repeat (char '|')) (map ppConsDecl cs) -- |pretty print a single constructor ppConsDecl :: ConsDecl -> Doc ppConsDecl (Cons qn _ _ tys) = fsep $ ppPrefixOp qn : map (ppTypeExpr 2) tys -- |pretty-print a type expression ppTypeExpr :: Int -> TypeExpr -> Doc ppTypeExpr _ (TVar v) = ppTVarIndex v ppTypeExpr p (FuncType ty1 ty2) = parenIf (p > 0) $ fsep [ppTypeExpr 1 ty1, rarrow, ppTypeExpr 0 ty2] ppTypeExpr p (TCons qn tys) = parenIf (p > 1 && not (null tys)) $ fsep (ppPrefixOp qn : map (ppTypeExpr 2) tys) ppTypeExpr p (ForallType vs ty) | null vs = ppTypeExpr p ty | otherwise = parenIf (p > 0) $ ppQuantifiedVars vs <+> ppTypeExpr 0 ty -- |pretty-print explicitly quantified type variables ppQuantifiedVars :: [TVarIndex] -> Doc ppQuantifiedVars vs | null vs = empty | otherwise = text "forall" <+> hsep (map ppTVarIndex vs) <+> char '.' -- |pretty-print a type variable ppTVarIndex :: TVarIndex -> Doc ppTVarIndex i = text $ vars !! i where vars = [ if n == 0 then [c] else c : show n | n <- [0 :: Int ..], c <- ['a' .. 'z'] ] -- |pretty-print a function declaration ppFuncDecl :: FuncDecl -> Doc ppFuncDecl (Func qn _ _ ty r) = hsep [ppPrefixOp qn, text "::", ppTypeExpr 0 ty] $+$ ppPrefixOp qn <+> ppRule r -- |pretty-print a function rule ppRule :: Rule -> Doc ppRule (Rule vs e) = fsep (map ppVarIndex vs) <+> equals <+> indent (ppExpr 0 e) ppRule (External _) = text "external" -- |pretty-print an expression ppExpr :: Int -> Expr -> Doc ppExpr _ (Var v) = ppVarIndex v ppExpr _ (Lit l) = ppLiteral l ppExpr p (Comb _ qn es) = ppComb p qn es ppExpr p (Free vs e) | null vs = ppExpr p e | otherwise = parenIf (p > 0) $ sep [ text "let" <+> list (map ppVarIndex vs) <+> text "free" , text "in" <+> ppExpr 0 e ] ppExpr p (Let ds e) = parenIf (p > 0) $ sep [text "let" <+> ppDecls ds, text "in" <+> ppExpr 0 e] ppExpr p (Or e1 e2) = parenIf (p > 0) $ ppExpr 1 e1 <+> text "?" <+> ppExpr 1 e2 ppExpr p (Case ct e bs) = parenIf (p > 0) $ ppCaseType ct <+> ppExpr 0 e <+> text "of" $$ indent (vcat (map ppBranch bs)) ppExpr p (Typed e ty) = parenIf (p > 0) $ ppExpr 0 e <+> text "::" <+> ppTypeExpr 0 ty -- |pretty-print a variable ppVarIndex :: VarIndex -> Doc ppVarIndex i = text $ 'v' : show i -- |pretty-print a literal ppLiteral :: Literal -> Doc ppLiteral (Intc i) = integer i ppLiteral (Floatc f) = double f ppLiteral (Charc c) = text (showEscape c) -- |Escape character literal showEscape :: Char -> String showEscape c | o < 10 = "'\\00" ++ show o ++ "'" | o < 32 = "'\\0" ++ show o ++ "'" | o == 127 = "'\\127'" | otherwise = show c where o = ord c -- |Pretty print a constructor or function call ppComb :: Int -> QName -> [Expr] -> Doc ppComb _ qn [] = ppPrefixOp qn ppComb p qn [e1,e2] | isInfixOp qn = parenIf (p > 0) $ hsep [ppExpr 1 e1, ppInfixOp qn, ppExpr 1 e2] ppComb p qn es = parenIf (p > 0) $ hsep (ppPrefixOp qn : map (ppExpr 1) es) -- |pretty-print a list of declarations ppDecls :: [(VarIndex, Expr)] -> Doc ppDecls = vcat . map ppDecl -- |pretty-print a single declaration ppDecl :: (VarIndex, Expr) -> Doc ppDecl (v, e) = ppVarIndex v <+> equals <+> ppExpr 0 e -- |pretty-print the type of a case expression ppCaseType :: CaseType -> Doc ppCaseType Rigid = text "case" ppCaseType Flex = text "fcase" -- |pretty-print a case branch ppBranch :: BranchExpr -> Doc ppBranch (Branch p e) = ppPattern p <+> rarrow <+> ppExpr 0 e -- |pretty-print a pattern ppPattern :: Pattern -> Doc ppPattern (Pattern c [v1,v2]) | isInfixOp c = ppVarIndex v1 <+> ppInfixOp c <+> ppVarIndex v2 ppPattern (Pattern c vs) = fsep (ppPrefixOp c : map ppVarIndex vs) ppPattern (LPattern l) = ppLiteral l -- Names -- |pretty-print a prefix operator ppPrefixOp :: QName -> Doc ppPrefixOp qn = parenIf (isInfixOp qn) (ppQName qn) -- |pretty-print a name in infix manner ppInfixOp :: QName -> Doc ppInfixOp qn = if isInfixOp qn then ppQName qn else bquotes (ppQName qn) -- |pretty-print a qualified name ppQName :: QName -> Doc ppQName (m, i) = text $ m ++ '.' : i -- |Check whether an operator is an infix operator isInfixOp :: QName -> Bool isInfixOp = all (`elem` "~!@#$%^&*+-=<>:?./|\\") . snd -- Indentation indent :: Doc -> Doc indent = nest 2