module Language.Haskell.Parens
(
pretty, Cify, cifyExp, unCify, hasOpInfo
)
where
import Language.Haskell.Syntax
import Language.Haskell.Pretty (prettyPrint)
pretty :: HsExp -> String
pretty = prettyPrint . unCify . cifyExp
data Assoc = LAssoc | NAssoc | RAssoc deriving ( Eq )
type OpInfo = (Assoc, Int)
type Context = (OpInfo, Bool)
type CExp = Context -> HsExp
oper :: OpInfo -> CExp -> CExp
oper i@(asi,pri) cexp ((aso,pro),onR) = mbParens (cexp c')
where
mbParens | needParens = HsParen
| otherwise = id
c' = (i,False)
needParens = pro > pri || (pro == pri && conflict)
conflict = aso /= asi || onR /= (aso == RAssoc)
reset :: CExp -> CExp
reset = oper (NAssoc,0)
onRight :: Bool -> CExp -> CExp
onRight onR cexp (opi,_) = cexp (opi,onR)
lift1 :: (a->b) -> (z->a) -> (z->b)
lift1 = (.)
lift2 :: (a->b->c) -> (z->a) -> (z->b) -> (z->c)
lift2 f h1 h2 c = f (h1 c) (h2 c)
lift3 :: (a->b->c->d) -> (z->a) -> (z->b) -> (z->c) -> (z->d)
lift3 f h1 h2 h3 c = f (h1 c) (h2 c) (h3 c)
type Cify a = a -> (Context -> a)
unCify :: (Context -> a) -> a
unCify = ($ ((NAssoc,0),False))
cifyExp :: Cify HsExp
cifyExp e =
case e of
HsVar _ -> const e
HsCon _ -> const e
HsLit _ -> const e
HsLambda loc pats body -> reset $
lift1 (HsLambda loc pats) (cifyExp body)
HsInfixApp el op er -> oper (opInfo (opName op)) $
lift2 (flip HsInfixApp op)
(onRight False (cifyExp el))
(onRight True (cifyExp er))
HsApp fun arg -> oper (opInfo (HsSymbol "")) $
lift2 HsApp
(onRight False (cifyExp fun))
(onRight True (cifyExp arg))
HsNegApp arg -> oper (opInfo (HsSymbol "-")) $
lift1 HsNegApp (onRight True (cifyExp arg))
HsLet decls body -> reset $
lift2 HsLet
(cifyDecls decls)
(cifyExp body)
HsIf c a b -> reset $
lift3 HsIf (cifyExp c) (cifyExp a) (cifyExp b)
HsTuple exps -> reset $
lift1 HsTuple (cifyExps exps)
HsParen e' -> cifyExp e'
_ -> error $ "cifyExp: unhandled case " ++ show e
cifyExps :: Cify [HsExp]
cifyExps exps c = map (flip cifyExp c) exps
cifyDecls :: Cify [HsDecl]
cifyDecls decls c = map (flip cifyDecl c) decls
cifyDecl :: Cify HsDecl
cifyDecl (HsPatBind loc pat rhs []) =
lift1 (\ r -> HsPatBind loc pat r [])
(cifyRhs rhs)
cifyDecl decl = error $ "cifyDecl: unhandled case " ++ show decl
cifyRhs :: Cify HsRhs
cifyRhs (HsUnGuardedRhs expr) = lift1 HsUnGuardedRhs (cifyExp expr)
cifyRhs rhs = error $ "cifyRhs: unhandled case " ++ show rhs
opQName :: HsQOp -> HsQName
opQName (HsQVarOp qname) = qname
opQName (HsQConOp qname) = qname
opName :: HsQOp -> HsName
opName = getName . opQName
getName :: HsQName -> HsName
getName (UnQual s) = s
getName (Qual _ s) = s
getName (Special HsCons) = HsSymbol ":"
getName (Special HsFunCon) = HsSymbol "->"
getName (Special s) = HsIdent (specialName s)
specialName :: HsSpecialCon -> String
specialName HsUnitCon = "()"
specialName HsListCon = "[]"
specialName HsFunCon = "->"
specialName (HsTupleCon n) = "(" ++ replicate (n1) ',' ++ ")"
specialName HsCons = ":"
apPrec :: Int
apPrec = 10
opInfo :: HsName -> OpInfo
opInfo name = case name of
HsSymbol "" -> (LAssoc, apPrec)
HsSymbol "." -> (RAssoc, 9)
HsSymbol "!!" -> (LAssoc, 9)
HsSymbol "^" -> (RAssoc, 8)
HsSymbol "^^" -> (RAssoc, 8)
HsSymbol "**" -> (RAssoc, 8)
HsSymbol "*" -> (LAssoc, 7)
HsSymbol "/" -> (LAssoc, 7)
HsIdent "quot" -> (LAssoc, 7)
HsIdent "rem" -> (LAssoc, 7)
HsIdent "div" -> (LAssoc, 7)
HsIdent "mod" -> (LAssoc, 7)
HsSymbol ":%" -> (LAssoc, 7)
HsSymbol "%" -> (LAssoc, 7)
HsSymbol "+" -> (LAssoc, 6)
HsSymbol "-" -> (LAssoc, 6)
HsSymbol ":" -> (RAssoc, 5)
HsSymbol "++" -> (RAssoc, 5)
HsSymbol "==" -> (NAssoc, 4)
HsSymbol "/=" -> (NAssoc, 4)
HsSymbol "<" -> (NAssoc, 4)
HsSymbol "<=" -> (NAssoc, 4)
HsSymbol ">=" -> (NAssoc, 4)
HsSymbol ">" -> (NAssoc, 4)
HsIdent "elem" -> (NAssoc, 4)
HsIdent "notElem" -> (NAssoc, 4)
HsSymbol "&&" -> (RAssoc, 3)
HsSymbol "||" -> (RAssoc, 2)
HsSymbol ">>" -> (LAssoc, 1)
HsSymbol ">>=" -> (LAssoc, 1)
HsSymbol "=<<" -> (RAssoc, 1)
HsSymbol "$" -> (RAssoc, 0)
HsSymbol "$!" -> (RAssoc, 0)
HsIdent "seq" -> (RAssoc, 0)
HsSymbol "<+>" -> (RAssoc, 5)
HsSymbol "***" -> (RAssoc, 3)
HsSymbol "&&&" -> (RAssoc, 3)
HsSymbol "+++" -> (RAssoc, 2)
HsSymbol "|||" -> (RAssoc, 2)
HsSymbol ">>>" -> (RAssoc, 1)
HsSymbol "^>>" -> (RAssoc, 1)
HsSymbol ">>^" -> (RAssoc, 1)
HsSymbol "<<<" -> (RAssoc, 1)
HsSymbol "^<<" -> (RAssoc, 1)
HsSymbol "<<^" -> (RAssoc, 1)
HsSymbol "*^" -> (LAssoc, 7)
HsSymbol "^+^" -> (LAssoc, 6)
_ -> (NAssoc, 1)
hasOpInfo :: HsName -> Bool
hasOpInfo name = snd (opInfo name) >= 0