module Pretty ( Pretty, PprStyle(..), ppNil, ppStr, ppChar, ppInt, ppInteger, ppDouble, ppSP, pp'SP, ppLbrack, ppRbrack, ppLparen, ppRparen, ppSemi, ppComma, ppCat, ppBeside, ppBesides, ppAbove, ppAboves, ppNest, ppSep, ppHang, ppInterleave, ppShow, ppUnformatted, -- abstract type, to complete the interface... PrettyRep ) where import CharSeq ppNil :: Pretty ppSP, pp'SP, ppLbrack, ppRbrack, ppLparen, ppRparen, ppSemi, ppComma :: Pretty ppStr :: [Char] -> Pretty ppChar :: Char -> Pretty ppInt :: Int -> Pretty ppInteger :: Integer -> Pretty ppDouble :: Double -> Pretty ppBeside :: Pretty -> Pretty -> Pretty ppBesides :: [Pretty] -> Pretty ppBesideSP :: Pretty -> Pretty -> Pretty ppCat :: [Pretty] -> Pretty -- i.e., ppBesidesSP ppAbove :: Pretty -> Pretty -> Pretty ppAboves :: [Pretty] -> Pretty ppInterleave :: Pretty -> [Pretty] -> Pretty ppSep :: [Pretty] -> Pretty ppHang :: Pretty -> Int -> Pretty -> Pretty ppNest :: Int -> Pretty -> Pretty ppShow :: Int -> Pretty -> [Char] ppUnformatted :: Pretty -> [Char] type Pretty = Int -- The width to print in -> Bool -- True => vertical context -> PrettyRep data PrettyRep = MkPrettyRep CSeq -- The text Int -- No of chars in last line Bool -- True if empty object Bool -- Fits on a single line in specified width --deriving () ppShow width p = cShow seq where (MkPrettyRep seq ll emp sl) = p width False {- !!! this seems to tickle an nhc bug (works w/ hbc) = case (p width False) of MkPrettyRep seq sl ll -> cShow seq -} ppUnformatted p = cShow seq where (MkPrettyRep seq ll emp sl) = p 80 False -- ToDo: ppUnformatted doesn't do anything yet ppNil width is_vert = MkPrettyRep cNil 0 True (width >= 0) -- Doesn't fit if width < 0, otherwise, ppNil -- will make ppBesides always return True. ppStr s width is_vert = MkPrettyRep (cStr s) ls False (width >= ls) where ls = length s ppChar c width is_vert = MkPrettyRep (cCh c) 1 False (width >= 1) ppInt n = ppStr (show n) ppInteger n = ppStr (show n) ppDouble n = ppStr (show n) ppSP = ppChar ' ' pp'SP = ppStr ", " ppLbrack = ppChar '[' ppRbrack = ppChar ']' ppLparen = ppChar '(' ppRparen = ppChar ')' ppSemi = ppChar ';' ppComma = ppChar ',' ppInterleave sep ps = ppSep (pi ps) where pi [] = [] pi [x] = [x] pi (x:xs) = (ppBeside x sep) : pi xs ppBeside p1 p2 width is_vert = MkPrettyRep (seq1 `cAppend` (cIndent ll1 seq2)) (ll1 + ll2) (emp1 `andL` emp2) ((width >= 0) `andL` (sl1 `andL` sl2)) -- This sequence of andL's ensures that ppBeside -- returns a False for sl as soon as possible. where MkPrettyRep seq1 ll1 emp1 sl1 = p1 width False MkPrettyRep seq2 ll2 emp2 sl2 = p2 (width-ll1) False -- ToDo: if emp{1,2} then we really -- should be passing on "is_vert" to p{2,1}. ppBesides [] = ppNil ppBesides ps = foldr1 ppBeside ps ppBesideSP p1 p2 width is_vert = MkPrettyRep (seq1 `cAppend` (sp `cAppend` (cIndent li seq2))) (li + ll2) (emp1 `andL` emp2) ((width >= wi) `andL` (sl1 `andL` sl2)) where MkPrettyRep seq1 ll1 emp1 sl1 = p1 width False MkPrettyRep seq2 ll2 emp2 sl2 = p2 (width-li) False li, wi :: Int li = if emp1 then 0 else ll1+1 wi = if emp1 then 0 else 1 sp = if emp1 `orL` emp2 then cNil else (cCh ' ') ppCat [] = ppNil ppCat ps = foldr1 ppBesideSP ps ppAbove p1 p2 width is_vert = MkPrettyRep (seq1 `cAppend` (nl `cAppend` seq2)) ll2 -- ToDo: make ll depend on empties? (emp1 `andL` emp2) False where nl = if emp1 `orL` emp2 then cNil else cNL MkPrettyRep seq1 ll1 emp1 sl1 = p1 width True MkPrettyRep seq2 ll2 emp2 sl2 = p2 width True -- ToDo: ditto about passing is_vert if empties ppAboves [] = ppNil ppAboves ps = foldr1 ppAbove ps ppNest n p width False = p width False ppNest n p width True = MkPrettyRep (cIndent n seq) (ll+n) emp sl where MkPrettyRep seq ll emp sl = p (width-n) True ppHang p1 n p2 width is_vert -- This is a little bit stricter than it could -- be made with a little more effort. -- Eg the output always starts with seq1 = if emp1 then p2 width is_vert else if (ll1 <= n) `orL` sl2 then -- very ppBesideSP'ish -- Hang it if p1 shorter than indent or if it doesn't fit MkPrettyRep (seq1 `cAppend` (cCh ' ') `cAppend` (cIndent (ll1+1) seq2)) (ll1 + 1 + ll2) False (sl1 `andL` sl2) else -- Nest it (pretty ppAbove-ish) MkPrettyRep (seq1 `cAppend` (cNL `cAppend` (cIndent n seq2'))) ll2' -- ToDo: depend on empties False False where MkPrettyRep seq1 ll1 emp1 sl1 = p1 width False MkPrettyRep seq2 ll2 emp2 sl2 = p2 (width-(ll1+1)) False -- ToDo: more "is_vert if empty" stuff MkPrettyRep seq2' ll2' emp2' sl2' = p2 (width-n) False -- ToDo: True? ppSep [] width is_vert = ppNil width is_vert ppSep [p] width is_vert = p width is_vert ppSep ps width is_vert = if sl then pr -- Fits on one line else ppAboves ps width is_vert -- Takes several lines where pr@(MkPrettyRep seq ll emp sl) = ppCat ps width is_vert {- !!! suspected on same nhc-bug grounds = case (ppBesides ps width is_vert) of pr1@(MkPrettyRep seq1 sl1 ll1) -> if (sl1 && ll1 <= width) then pr1 -- Fits on one line else ppAboves ps width is_vert -- Takes several lines -} andL :: Bool -> Bool -> Bool andL False x = False andL True x = x orL :: Bool -> Bool -> Bool orL True x = True orL False x = x data PprStyle = PprForUser -- Pretty-print in a way that will make sense -- to the ordinary user; must be very close to Haskell -- syntax, etc. -- ToDo: how diff is this from what pprInterface must do? | PprDebug -- Standard debugging output | PprShowAll -- Debugging output which leaves nothing to the imagination | PprInterface -- Interface generation