    {-# OPTIONS_GHC -fglasgow-exts #-}

    -- Monadic front-end to Text.PrettyPrint.HughesPJ

    module Language.Haskell.TH.PprLib (

    	-- * The document type
            Doc,            -- Abstract, instance of Show
            PprM,

    	-- * Primitive Documents
            empty,
            semi, comma, colon, space, equals,
            lparen, rparen, lbrack, rbrack, lbrace, rbrace,

    	-- * Converting values into documents
            text, char, ptext,
            int, integer, float, double, rational,

    	-- * Wrapping documents in delimiters
            parens, brackets, braces, quotes, doubleQuotes,

    	-- * Combining documents
            (<>), (<+>), hcat, hsep,
            ($$), ($+$), vcat,
            sep, cat,
            fsep, fcat,
    	nest,
            hang, punctuate,

    	-- * Predicates on documents
    	isEmpty,

        to_HPJ_Doc, pprName, pprName'
      ) where

    import Language.Haskell.TH.Syntax
        (Name(..), showName', NameFlavour(..), NameIs(..))
    import qualified Text.PrettyPrint.HughesPJ as HPJ
    import Control.Monad (liftM, liftM2)
    import Data.Map ( Map )
    import qualified Data.Map as Map ( lookup, insert, empty )
    import GHC.Base (Int(..))

    infixl 6 <>
    infixl 6 <+>
    infixl 5 $$, $+$

    -- ---------------------------------------------------------------------------
    -- The interface

    -- The primitive Doc values

    instance Show Doc where
       show d = HPJ.render (to_HPJ_Doc d)

    isEmpty :: Doc    -> PprM Bool;  -- ^ Returns 'True' if the document is empty

    empty   :: Doc;			-- ^ An empty document
    semi	:: Doc;			-- ^ A ';' character
    comma	:: Doc;			-- ^ A ',' character
    colon	:: Doc;			-- ^ A ':' character
    space	:: Doc;			-- ^ A space character
    equals	:: Doc;			-- ^ A '=' character
    lparen	:: Doc;			-- ^ A '(' character
    rparen	:: Doc;			-- ^ A ')' character
    lbrack	:: Doc;			-- ^ A '[' character
    rbrack	:: Doc;			-- ^ A ']' character
    lbrace	:: Doc;			-- ^ A '{' character
    rbrace	:: Doc;			-- ^ A '}' character

    text	 :: String   -> Doc
    ptext	 :: String   -> Doc
    char 	 :: Char     -> Doc
    int      :: Int      -> Doc
    integer  :: Integer  -> Doc
    float    :: Float    -> Doc
    double   :: Double   -> Doc
    rational :: Rational -> Doc

    parens       :: Doc -> Doc; 	-- ^ Wrap document in @(...)@
    brackets     :: Doc -> Doc;  	-- ^ Wrap document in @[...]@
    braces	     :: Doc -> Doc;   	-- ^ Wrap document in @{...}@
    quotes	     :: Doc -> Doc;	-- ^ Wrap document in @\'...\'@
    doubleQuotes :: Doc -> Doc;	-- ^ Wrap document in @\"...\"@

    -- Combining @Doc@ values

    (<>)   :: Doc -> Doc -> Doc;     -- ^Beside
    hcat   :: [Doc] -> Doc;          -- ^List version of '<>'
    (<+>)  :: Doc -> Doc -> Doc;     -- ^Beside, separated by space
    hsep   :: [Doc] -> Doc;          -- ^List version of '<+>'

    ($$)   :: Doc -> Doc -> Doc;     -- ^Above; if there is no
                                    -- overlap it \"dovetails\" the two
    ($+$)   :: Doc -> Doc -> Doc;	 -- ^Above, without dovetailing.
    vcat   :: [Doc] -> Doc;          -- ^List version of '$$'

    cat    :: [Doc] -> Doc;          -- ^ Either hcat or vcat
    sep    :: [Doc] -> Doc;          -- ^ Either hsep or vcat
    fcat   :: [Doc] -> Doc;          -- ^ \"Paragraph fill\" version of cat
    fsep   :: [Doc] -> Doc;          -- ^ \"Paragraph fill\" version of sep

    nest   :: Int -> Doc -> Doc;     -- ^ Nested

    -- GHC-specific ones.

    hang :: Doc -> Int -> Doc -> Doc;	-- ^ @hang d1 n d2 = sep [d1, nest n d2]@
    punctuate :: Doc -> [Doc] -> [Doc];      -- ^ @punctuate p [d1, ... dn] = [d1 \<> p, d2 \<> p, ... dn-1 \<> p, dn]@

    -- ---------------------------------------------------------------------------
    -- The "implementation"

    type State = (Map Name Name, Int)
    data PprM a = PprM { runPprM :: State -> (a, State) }

    pprName :: Name -> Doc
    pprName = pprName' Alone

    pprName' :: NameIs -> Name -> Doc
    pprName' ni n@(Name o (NameU _))
     = PprM $ \s@(fm, i@(I# i'))
            -> let (n', s') = case Map.lookup n fm of
                             Just d -> (d, s)
                             Nothing -> let n' = Name o (NameU i')
                                        in (n', (Map.insert n n' fm, i + 1))
               in (HPJ.text $ showName' ni n', s')
    pprName' ni n = text $ showName' ni n

    {-
    instance Show Name where
      show (Name occ (NameU u))    = occString occ ++ "_" ++ show (I# u)
      show (Name occ NameS)        = occString occ
      show (Name occ (NameG ns m)) = modString m ++ "." ++ occString occ

    data Name = Name OccName NameFlavour

    data NameFlavour
      | NameU Int#			-- A unique local name
    -}

    to_HPJ_Doc :: Doc -> HPJ.Doc
    to_HPJ_Doc d = fst $ runPprM d (Map.empty, 0)

    instance Monad PprM where
        return x = PprM $ \s -> (x, s)
        m >>= k  = PprM $ \s -> let (x, s') = runPprM m s
                                in runPprM (k x) s'

    type Doc = PprM HPJ.Doc

    -- The primitive Doc values

    isEmpty = liftM HPJ.isEmpty

    empty = return HPJ.empty
    semi = return HPJ.semi
    comma = return HPJ.comma
    colon = return HPJ.colon
    space = return HPJ.space
    equals = return HPJ.equals
    lparen = return HPJ.lparen
    rparen = return HPJ.rparen
    lbrack = return HPJ.lbrack
    rbrack = return HPJ.rbrack
    lbrace = return HPJ.lbrace
    rbrace = return HPJ.rbrace

    text = return . HPJ.text
    ptext = return . HPJ.ptext
    char = return . HPJ.char
    int = return . HPJ.int
    integer = return . HPJ.integer
    float = return . HPJ.float
    double = return . HPJ.double
    rational = return . HPJ.rational

    parens = liftM HPJ.parens
    brackets = liftM HPJ.brackets
    braces = liftM HPJ.braces
    quotes = liftM HPJ.quotes
    doubleQuotes = liftM HPJ.doubleQuotes

    -- Combining @Doc@ values

    (<>) = liftM2 (HPJ.<>)
    hcat = liftM HPJ.hcat . sequence
    (<+>) = liftM2 (HPJ.<+>)
    hsep = liftM HPJ.hsep . sequence

    ($$) = liftM2 (HPJ.$$)
    ($+$) = liftM2 (HPJ.$+$)
    vcat = liftM HPJ.vcat . sequence

    cat  = liftM HPJ.cat . sequence
    sep  = liftM HPJ.sep . sequence
    fcat = liftM HPJ.fcat . sequence
    fsep = liftM HPJ.fsep . sequence

    nest n = liftM (HPJ.nest n)

    hang d1 n d2 = do d1' <- d1
                      d2' <- d2
                      return (HPJ.hang d1' n d2')

    -- punctuate uses the same definition as Text.PrettyPrint.HughesPJ
    punctuate p []     = []
    punctuate p (d:ds) = go d ds
                       where
                         go d [] = [d]
                         go d (e:es) = (d <> p) : go e es