{-
Copyright(C) 1999 Erik Meijer
-}
module Pretty where

{-

Quick reference for the simple Pretty-print Combinators

  |---|     |----|   |-------|
  |koe| <|> |beer| = |koebeer|
  |---|     |----|   |-------|

  |---|     |----|   |--------|
  |koe| <+> |beer| = |koe beer|
  |---|     |----|   |--------|

  |---|     |----|   |----|
  |koe| <-> |beer| = |koe |
  |---|     |----|   |beer|
                     |----|

  |---|            |----|   |-------|
  |koe| <|> nest 2 |beer| = |koebeer|
  |---|            |----|   |-------|

  |---|            |----|   |------|
  |koe| <-> nest 2 |beer| = |koe   |
  |---|            |----|   |  beer|
                            |------|
                            
  empty =                            
-}

{-

Extremely simplified version of John Hughes' combinators, 
without (sep), but with (empty).

TODO: use Okasaki-style catenable dequeues to represent Doc

(c) Erik Meijer and Arjan van IJzendoorn

October 199

-}

infixl 7 <+>
infixl 6 <|>
infixr 5 <->
  
instance Show Doc where
  { showsPrec = showsPrecDoc }

showsPrecDoc i = \d ->
  case d of
    { Empty -> id
    ; Doc ds -> layout ds
    }
 
data Doc
  = Doc [(Int,ShowS)]
  | Empty
  
layout :: [(Int,ShowS)] -> ShowS
layout = \ds ->
  case ds of
    { []       -> showString ""
    ; [(n,s)]  -> indent n.s
    ; (n,s):ds -> indent n.s.showString "\n".layout ds
    }

width :: Doc -> Int
width = \d ->
  case d of
    { Empty -> 0
    ; Doc ds -> maximum [ i + length (s "") | (i,s) <- ds ]
    }
  
text :: String -> Doc
text = \s -> Doc [(0,showString s)]

nest :: Int -> Doc -> Doc
nest n = \d ->
  case d of
    { Empty -> Empty
    ; Doc ds -> Doc [ (i+n,d) | (i,d) <- ds ]
    }

(<->) :: Doc -> Doc -> Doc
Empty <-> Empty = Empty
Empty <-> (Doc d2) = Doc d2
(Doc d1) <-> Empty = Doc d1
(Doc d1) <-> (Doc d2) = Doc (d1++d2)

(<+>) :: Doc -> Doc -> Doc
a <+> b = a <|> (text " ") <|> b

(<|>) :: Doc -> Doc -> Doc
Empty <|> Empty = Empty
Empty <|> (Doc d2) = Doc d2
(Doc d1) <|> Empty = Doc d1
(Doc d1) <|> (Doc d2) =
  let 
    { (d,(i,s)) = (init d1,last d1)
    ; ((j,t),e) = (head d2,tail d2)
    }
  in
    (    Doc d 
     <-> Doc [(i,s.t)] 
     <-> nest (i + length (s "") - j) (Doc e)
    )
    
-- Derived operations

empty :: Doc
empty = Empty

{-

horizontal s [a,b,c] =
  a <|> (s <|> b) <|> (s <|> c)

-}

horizontal :: Doc -> [Doc] -> Doc
horizontal s = \ds ->
  case ds of
    { [] -> empty
    ; ds -> foldr1 (\d -> \ds -> d <|> s <|> ds) ds
    }

{-

vertical s [a,b,c] =
  a
  <->
  (s <|> b)
  <->
  (s <|> c)

-}

vertical :: [Doc] -> Doc
vertical = \ds ->
  case ds of
    { [] -> empty
    ; d:ds -> d <-> vertical ds
    }

block (o,s,c) = \ds ->
  case ds of
    { [] -> o<|>c
    ; [d] -> o<|>d<|>c
    ; d:ds -> (vertical ((o <|> d):[s <|> d | d <- ds ])) <-> c
    }
    
-- Helper function

indent :: Int -> ShowS
indent = \n ->
  showString [ ' ' | i <- [1..n] ]
