\begin{code}
module Text.Printf.TH.Simplify (simplify, Simplify) where

import Language.Haskell.TH.Syntax
import Language.Haskell.TH
import Monad (liftM)

class Simplify a where
    simplify :: a -> a

instance Simplify a => Simplify (Q a) where
    simplify = liftM simplify

instance Simplify a => Simplify [a] where
    simplify = map simplify

instance Simplify Exp where
    --simplify (VarE "GHC.Base:otherwise") = ConE ''True
    simplify (AppE e1 e2) = AppE (simplify e1) (simplify e2)

    simplify (InfixE me1 (VarE op) me2)
     = let (me1', me2') = (fmap simplify me1, fmap simplify me2)
       in f me1' op me2' 
          where 
              f (Just (LitE (IntegerL i1))) x (Just (LitE (IntegerL i2))) | x == '(>) =
                    if i1 > i2 then ConE 'True else ConE 'False
              f (Just (LitE (IntegerL i1))) x (Just (LitE (IntegerL i2))) | x ==  '(>=) =
                     if i1 >= i2 then ConE 'True
                                 else ConE 'False
              f (Just (LitE (IntegerL i1))) x ( Just (LitE (IntegerL i2))) | x ==  '(<) =
                     if i1 < i2 then ConE 'True
                                else ConE 'False
              f (Just (LitE (IntegerL i1))) x (Just (LitE (IntegerL i2))) | x == '(<=) =
                     if i1 <= i2 then ConE 'True
                                 else ConE 'False
              f (Just (LitE (IntegerL 0))) x (Just y) | x == '(+) = y
              f (Just y) x (Just (LitE (IntegerL 0))) | x == '(+) = y
              f (Just (LitE (IntegerL i1))) x (Just (LitE (IntegerL i2))) | x == '(+) =
                     LitE (IntegerL (i1 + i2))
              f (Just (LitE (IntegerL 0))) x (Just y) | x == '(-) = AppE (VarE 'negate) y
              f (Just y) x (Just (LitE (IntegerL 0)))  | x == '(-) = y
              f (Just (LitE (IntegerL i1))) x (Just (LitE (IntegerL i2))) | x == '(-) =
                     LitE (IntegerL (i1 - i2))
              f (Just (LitE (IntegerL 1))) x (Just y) | x == '(*) = y
              f (Just y) x (Just (LitE (IntegerL 1))) | x == '(*) = y
              f (Just (LitE (IntegerL i1))) x (Just (LitE (IntegerL i2))) | x == '(*) =
                     LitE (IntegerL (i1 * i2))
              f me1' _ me2' = InfixE me1' (VarE op) me2'
    simplify (CondE g t f) = let x = simplify g in
      f' x where f' (ConE y) | y == 'True = simplify t
                 f' (ConE y) | y == 'False = simplify f
                 f' g' = CondE g' (simplify t) (simplify f)
    simplify (LamE ps e) = LamE ps (simplify e)
    simplify (TupE es) = TupE (map simplify es)
    -- Should subst literals/vars?
    simplify (LetE ds e) = LetE (simplify ds) (simplify e)
    simplify (CaseE e ms) = CaseE (simplify e) (map simplify ms)
    simplify (DoE ss) = DoE (map simplify ss)
    simplify (CompE ss) = CompE (map simplify ss)
    simplify (ArithSeqE dd) = ArithSeqE (simplify dd)
    simplify (ListE es) = ListE (map simplify es)
    simplify (SigE e t) = SigE (simplify e) t
    simplify e = e

instance Simplify Dec where
    simplify (FunD f cs) = FunD f (map simplify cs)
    simplify (ValD p rhs ds) = ValD p (simplify rhs) (simplify ds)
    simplify (ClassD ctxt tycon [] tyvars ds) -- [] added correctly ? Marc
        = ClassD ctxt tycon [] tyvars (simplify ds)
    simplify (InstanceD ctxt typ ds) = InstanceD ctxt typ (simplify ds)
    simplify d = d

instance Simplify Body where
    simplify (NormalB e) = NormalB (simplify e)
    simplify (GuardedB ges)
     = GuardedB (map (\(e1, e2) -> (simplify e1, simplify e2)) ges)

instance Simplify Guard where
    simplify (NormalG e) = NormalG $ simplify e
    simplify (PatG l) = PatG $ map simplify l    

instance Simplify Range where
    simplify (FromR e) = FromR (simplify e)
    simplify (FromThenR e1 e2) = FromThenR (simplify e1) (simplify e2)
    simplify (FromToR e1 e2) = FromToR (simplify e1) (simplify e2)
    simplify (FromThenToR e1 e2 e3)
     = FromThenToR (simplify e1) (simplify e2) (simplify e3)

instance Simplify Stmt where
    simplify (BindS p e) = BindS p (simplify e)
    simplify (LetS ds) = LetS (simplify ds)
    simplify (NoBindS e) = NoBindS (simplify e)
    simplify (ParS _) = error "simplify[Statement]: ParS"

instance Simplify Match where
    simplify (Match p rhs ds) = Match p (simplify rhs) (simplify ds)

instance Simplify Clause where
    simplify (Clause ps rhs ds) = Clause ps (simplify rhs) (simplify ds)
\end{code}