hfusion-0.0.5: HFusion/Internal/HsPretty.hs
-- Please, see the file LICENSE for copyright and license information.
{-# LANGUAGE PatternGuards #-}
module HFusion.Internal.HsPretty(
show, -- :: Prog -> String
ShowDoc(..),
showTuple, -- :: ShowDoc a => [a]->Doc
ttupleprec,tlambprec,tletprec,tcaseprec,tfappprec,thyloprec,tcappprec,tappprec,tsumprec,
mergeCasePatterns,
module Text.PrettyPrint,
sss
-- :: Term -> String
) where
import HFusion.Internal.HsSyn
import Text.PrettyPrint
import HFusion.Internal.HsPrec
import HFusion.Internal.Utils
import Control.Monad(mplus,msum)
import Control.Arrow((***))
import Data.List(transpose,intersperse)
import Data.Char(isLetter)
import HFusion.Internal.Messages
instance Show Prog where
show (Prog defs) = render.vcat$ intersperse (text "")$ map showDoc defs
{- o : show (Prog defs) = fullRender PageMode 100 1.5 string_txt "" (showDoc defs)
string_txt (Chr c) s = c:s
string_txt (Str s1) s2 = s1 ++ s2
string_txt (PStr s1) s2 = s1 ++ s2
-}
instance Show Def where
show = render . showDoc
instance Show Term where
show t = render.showDoc$ t
-- | A class for building 'Doc' values.
class ShowDoc a where
showDoc::a->Doc
showDocPrec:: Precedence -> LeftParam -> a -> Doc
showDoc = showDocPrec (0,None) False
showDocPrec _ _ = showDoc
showTuple :: ShowDoc a => [a]->Doc
showTuple [a] = showDoc a
showTuple ls = char '('<> (hcat$ intersperse (char ',')$ map showDoc ls)<>char ')'
parlist:: Bool -> [Doc] -> Doc
parlist paren content = if paren
then char '(' <>
cat (intersperse (text " ") content++[char ')'])
else sep content
tab,stab::Int
-- | normal indentation
tab=4
-- | small indentation
stab=2
-- | Precedences for the constructors of 'Term' values.
ttupleprec,tlambprec,tletprec,tcaseprec,
tfappprec,thyloprec,tcappprec,tappprec,tsumprec::Precedence
ttupleprec=(0,None)
tlambprec=(0,RightAsoc)
tletprec=(0,RightAsoc)
tcaseprec=(0,RightAsoc)
thyloprec=(0,RightAsoc)
tsumprec =(1,None)
tfappprec=(maxprec,LeftAsoc)
tcappprec=(maxprec,LeftAsoc)
tinfixcappprec=(9,RightAsoc)
tappprec=(maxprec,LeftAsoc)
dec (p,a) = (p-1,a)
maxprec::Int
maxprec=10
instance (ShowDoc a,ShowDoc b) => ShowDoc (a,b) where
showDoc (a,b) = char '('<>showDoc a<>char ','<+> showDoc b <>char ')'
instance ShowDoc Bool where
showDoc True = text "True"
showDoc False = text "False"
instance ShowDoc Def where
showDoc (Defvalue name t) = vcat$ map showDef$ splitCase vs t'
where getParams (Tlamb bv t) = let (l,t') = getParams t in (bv:l,t')
getParams t = ([],t)
(vs,t') = getParams t
-- tries to separate a case definition into equations
-- it returns a list containing one element for each derived equation
-- [(equation patterns,equation term)]
splitCase :: [Boundvar] -> Term -> [([Pattern],Term)]
splitCase bvs t = maybe [(map bv2pat bvs,t)] (uncurry$ zipWith (\p t->(patList p,t)))$
mergeCasePatterns' p t
where p = ptuple$ map bv2pat bvs
patList (Ptuple ps) = ps
patList p = [p]
showDef (ps,t) = sep [text (show name) <+> hsep (map (toDoc (vars t)) ps) <+> char '='
, nest 8$ showDoc t]
toDoc vs p = showDocPrec tcappprec False (removeSpuriousPas vs p)
-- Flattens nested case terms by carefully replacing patterns into patterns to remove
-- inner case constructs and increase the amount of alternatives in the outer case
-- constructs.
mergeCasePatterns :: Term -> Term
mergeCasePatterns t = mergeCPat id t
where mergeCPat bvs (Tlamb bv t) = Tlamb bv$ mergeCPat (bvs . (bv:)) t
mergeCPat bvs t =
let ps = map bv2pat (bvs [])
pvs = lastVarsList ps
in if null ps
then case t of
Tcase t0 ps ts -> (\(ps,ts)-> Tcase t0 (concat ps) (concat ts))$
unzip$ map unzip$ zipWith ((map removePas.) . mergeCase) ps ts
_ -> t
else if isTcase t then
delCases$ uncurry (Tcase (ttuple (map Tvar pvs)))$ unzip$ map removePas$ mergeCase (ptuple$ map Pvar pvs) t
else t
mergeCase :: Pattern -> Term -> [(Pattern,Term)]
mergeCase p t = case mergeCasePatterns' p t of
Just (ps,ts) -> concat$ zipWith mergeCase ps ts
Nothing -> [(p,t)]
removePas (p,t) = (removeSpuriousPas (vars t) p,t)
isTcase (Tcase _ _ _) = True
isTcase _ = False
-- Removes Pas patterns which are not in a given set of variables.
removeSpuriousPas :: [Variable] -> Pattern -> Pattern
removeSpuriousPas vs (Pas v p@(Pas v' _)) | v==v' = removeSpuriousPas vs p
removeSpuriousPas vs (Pas v p@(Pvar v')) | v==v' = p
| p==pany = if elem v vs then Pvar v else pany
removeSpuriousPas vs (Pas v p) | elem v vs = Pas v (removeSpuriousPas vs p)
| otherwise = removeSpuriousPas vs p
removeSpuriousPas _ p@(Pvar _) = p
removeSpuriousPas _ p@(Plit _) = p
removeSpuriousPas vs (Ptuple ps) = Ptuple (map (removeSpuriousPas vs) ps)
removeSpuriousPas vs (Pcons c ps) = Pcons c (map (removeSpuriousPas vs) ps)
-- Merges the cases in the term to the given pattern. Returns Nothing
-- if there are no cases to merge.
mergeCasePatterns' :: Pattern -> Term -> Maybe ([Pattern],[Term])
mergeCasePatterns' p t = case removeAllCaseVar Nothing t of
Just res -> mergeCasePatterns'' p res `mplus` Just ([p],[res])
_ -> mergeCasePatterns'' p t
where removeAllCaseVar res t = maybe res (\mt->removeAllCaseVar (Just mt) mt)$ removeCaseVars t
removeCaseVars (Tcase t0 ps ts)
| length t0s > 1,
Just i<-msum (zipWith3 findVarPattern [0..] t0s pss) = Just (Tcase (listToTerm$ del i t0s) (map (removePattern i) ps) ts)
where t0s = termList t0
pss = transpose$ map patList ps
removeCaseVars _ = Nothing
findVarPattern i (Tvar v) ps | all (isPatternVar v) ps = Just i
findVarPattern _ _ _ = Nothing
isPatternVar v (Pvar v') = v==v'
isPatternVar _ _ = False
removePattern i (Ptuple ps) = toPat$ del i ps
removePattern _ _ = error "mergeCasePatterns': something that shouldn't had happened happened."
listToTerm [t] = t
listToTerm ts = Ttuple False ts
termList (Ttuple _ ts) = ts
termList t = [t]
patList (Ptuple ps) = ps
patList p = [p]
toPat [p] = p
toPat ps = Ptuple ps
del i ls = take i ls ++ drop (i+1) ls
mergeCasePatterns'' :: Pattern -> Term -> Maybe ([Pattern],[Term])
mergeCasePatterns'' p (Tcase t0 ps ts)
| all isAllowedTerm t0s
&& orderMatches lvp t0s
&& all ((==length t0s) . length) pss
&& any (not.null.fst) sustss =
Just (map (flip substitutePattern p . fst)$ sustss ,map snd sustss)
where t0s = termList t0
tvs = vars t0s
pss = map patList ps
sustss = zipWith (makeSustPairs tvs) pss ts
lvp = lastVars p
isAllowedTerm (Tvar v) = elem v lvp
isAllowedTerm _ = False
makeSustPairs tvs ps t = let ss = zipWith (makeSustPair t (vars t)) tvs ps
in (map fst ss,substitution (concat . map snd$ ss) t)
makeSustPair t _ v (Pvar v') | notElem v (varsB t) = ((v,Pvar v),[(v',Tvar v)])
makeSustPair _ tsvs v p | p/=pany = ((v,Pas v p),[])
| elem v tsvs = ((v,Pvar v),[])
| otherwise = ((v,pany),[])
patList (Ptuple ps) = ps
patList p = [p]
termList (Ttuple _ ts) = ts
termList t = [t]
orderMatches (v:lvp) t0s@(Tvar v':t0ss) | v==v' = orderMatches lvp t0ss
| otherwise = orderMatches lvp t0s
orderMatches _ [] = True
orderMatches _ _ = False
mergeCasePatterns'' _ _ = Nothing
substitutePattern :: [(Variable,Pattern)] -> Pattern -> Pattern
substitutePattern subst p@(Pvar v) = maybe p id $ lookup v subst
substitutePattern _ p@(Plit _) = p
substitutePattern subst (Ptuple ps) = Ptuple$ map (substitutePattern subst) ps
substitutePattern subst (Pcons c ps) = Pcons c$ map (substitutePattern subst) ps
substitutePattern subst (Pas v p) = Pas v$ substitutePattern subst p
-- lastVars returns the variables that can be replaced without breaking the pattern matching sequence.
-- e.g. in (C a (C2 b c) d) the variables that can be safely replaced are b c and d.
lastVars :: Pattern -> [Variable]
lastVars (Pvar v) = [v]
lastVars (Pcons _ ps) = lastVarsList ps
lastVars (Ptuple ps) = lastVarsList ps
lastVars (Plit _) = []
lastVars (Pas _ p) = lastVars p
lastVarsList ps = let (vps,others) = break (not.isPvar) (reverse ps)
in if null others then reverse (vars vps)
else lastVars (head others)++reverse (vars vps)
where isPvar (Pvar _) = True
isPvar _ = False
instance ShowDoc Variable where
showDoc v = text (show v)
instance ShowDoc Term where
showDocPrec _ _ (Tvar name) = text$ if not (null n) && not (isLetter (head n)) then "("++n++")" else n
where n = show name
showDocPrec _ _ (Tlit literal) = text (show literal)
showDocPrec _ _ (Ttuple _ [Tvar name]) = text (show name)
showDocPrec _ _ (Ttuple _ terms) =
let
tdocs = map (showDocPrec ttupleprec False) terms
in
cat $ [char '(']
++ map (nest 1) (uncurry(++)$ (id *** map (char ','<>))$ splitAt 1 tdocs)
++ [char ')']
showDocPrec prec left (Tlamb boundvar term) =
let
t=showDocPrec tlambprec False term
content = [char '\\' <> text (show boundvar) <+> text "->",
nest tab t]
paren = parenthesize prec left tlambprec
in parlist paren content
showDocPrec prec left (Tlet variable term1 term2) =
let
t1=showDocPrec tletprec False term1
t2=showDocPrec tletprec False term2
content = [(text "let" <+> text (show variable) <+> equals <+> t1)
$$ nest 1 (text "in" <+> t2)]
paren = parenthesize prec left tletprec
in parlist paren content
showDocPrec prec left (Tif term0 term1 term2) =
let
t0=showDocPrec tletprec False term0
t1=showDocPrec tletprec False term1
t2=showDocPrec tletprec False term2
content = [vcat [sep [text "if" <+> t0,nest stab (text "then"<+>t1)],
nest stab (text "else" <+> t2)]]
paren = parenthesize prec left tletprec
in parlist paren content
showDocPrec _ _ (Tpar t) = char '(' <+> showDocPrec (0,None) False t <+> char ')'
showDocPrec prec left (Tcase term ps' ts') =
let
tr=showDocPrec tcaseprec False term
ts=map (showDocPrec tcaseprec False) ts'
ps=map (showDocPrec tcaseprec False) ps'
content = [text "case" <+> tr <+> text "of",
nest stab $
vcat [sep [p <+> text "->", nest tab t]
| (p,t)<-zip ps ts]
]
paren = parenthesize prec left tcaseprec
in parlist paren content
showDocPrec p l (Tfapp variable [] ) = showDocPrec p l (Tvar variable)
showDocPrec prec left (Tfapp variable terms) =
let
tds@(td:tdocs)=map (flip (showDocPrec op_precedence)) terms
content
| infx && length terms>1 =
(td True <+> text (show variable)):
map (\t->nest 2$ t False) tdocs
| infx = [td True<>text (show variable)]
| otherwise = text (show variable):
map (\t->nest 2$ t False) tds
paren = infx && length terms<=1 || parenthesize prec left op_precedence
in
parlist paren content
where infx = isInfix variable
isInfix (Vgen _ _) = False
isInfix (Vuserdef ('@':'b':_)) = False
isInfix (Vuserdef (c:_)) = not ((('a'<=c)&&(c<='z'))||(c=='_'))
isInfix _ = error infix_Operator_Without_Characters_In_Name
op_precedence | infx && variable/=Vuserdef "/" && variable/=Vuserdef "-"
&& variable/=Vuserdef "+"
= (1,RightAsoc)
| otherwise = tfappprec
showDocPrec _ _ (Tcapp constructor []) = text$ if constructor/="[]" && not (null constructor)
&& not (isLetter (head constructor))
then "("++constructor++")"
else constructor
showDocPrec prec left (Tcapp constructor terms) =
let
tds@(td:tdocs)=map (flip (showDocPrec op_precedence)) terms
content
| isInfix && length terms>1 = td True <+> text constructor <+>
hsep (map (\t->t False) tdocs)
| isInfix = td True<>text constructor
| otherwise = text constructor <+>
hsep (map (\t->t False) tds)
paren = isInfix && length terms<=1 || parenthesize prec left op_precedence
in
hpar paren content
where isInfix = case constructor of
"_" -> False
(c:_)-> not (('A'<=c)&&(c<='Z')) && constructor/="[]"
_ -> error infix_Constructor_Without_Characters_In_Name
op_precedence | isInfix = tinfixcappprec
| otherwise = tcappprec
showDocPrec prec left (Tapp term1 term2) =
let
t1=showDocPrec tappprec True term1
t2=showDocPrec tappprec False term2
paren = parenthesize prec left tappprec
content = t1 <+> t2
in
hpar paren content
showDocPrec _ _ Tbottom = text "undef"
showDocPrec prec left (Thyloapp v i ts pos t) = showDocPrec prec left (Tfapp v (thyloArgs i ts pos t))
instance Show Boundvar where
show = render.showDoc
instance ShowDoc Boundvar where
showDoc (Bvar name) = text$ show name
showDoc (Bvtuple _ vars) = showTuple vars
instance Show Pattern where
show (Pvar name) = show name
show (Ptuple patterns) = "("++ (concat$ intersperse ","$ map show patterns) ++")"
show (Pcons name patterns) = "("++ name ++ foldr ((++).(" "++).show) "" patterns ++")"
show (Plit literal) = show literal
show (Pas v p) = show v++'@':show p
instance ShowDoc Pattern where
showDocPrec _ _ (Pvar v) = text (show v)
showDocPrec _ _ (Pas v p) = text (show v)<>char '@'<>showDocPrec tcappprec False p
showDocPrec _ _ (Ptuple patterns) =
let
ps=map (showDocPrec ttupleprec False) patterns
in
(char '('<>hcat (uncurry(++)$ (id *** map (char ','<>))$ splitAt 1 ps)<>char ')')
showDocPrec _ _ (Pcons name []) = text name
showDocPrec prec left (Pcons name patterns) =
let
tds@(td:tdocs)= map (flip (showDocPrec op_precedence)) patterns
content
| esinfijo name = td True <> text name <>
hsep (map (\t->t False) tdocs)
| otherwise = text name <+>
hsep (map (\t->t False) tds)
paren = parenthesize prec left op_precedence
in
hpar paren content
where esinfijo (c:_) = not (('A'<=c)&&(c<='Z'))
esinfijo _ = error infix_Constructor_Without_Characters_In_Name
op_precedence | esinfijo name = (fst tcappprec,RightAsoc)
| otherwise = tcappprec
showDocPrec _ _ (Plit literal) = text (show literal)
instance Show Literal where
show (Lstring string) = show string
show (Lint string) = string
show (Lchar char) = show char
show (Lrat string) = string