{-# LANGUAGE TemplateHaskell, RankNTypes, GADTs, PatternSynonyms #-}

module T8761 where

{- Testsuite for pattern synonyms as implemented by ticket #8761 -}

import Control.Monad
import Language.Haskell.TH
import System.IO

data Ex         where MkEx       :: forall a. a -> Ex
data ExProv     where MkExProv   :: forall a. (Show a) => a -> ExProv
data UnivProv a where MkUnivProv :: forall a. (Show a) => a -> UnivProv a

{- Test manual construction and pretty printing of pattern synonyms -}
do
  [qx1,qy1,qz1] <- mapM (\i -> newName $ "x" ++ show i) [1,2,3]
  let nm1       = mkName "Q1"
      prefixPat = patSynD nm1 (prefixPatSyn [qx1,qy1,qz1]) unidir
        (tupP [tupP [varP qx1, varP qy1], listP [varP qz1], wildP, wildP])

  [qx2,qy2] <- mapM (\i -> newName $ "x" ++ show i) [1,2]
  let nm2      = mkName "Q2"
      infixPat = patSynD nm2 (infixPatSyn qx2 qy2) implBidir
        (tupP [tupP [varP qx2, varP qy2]])

  let nm3           = mkName "Q3"
      [qx3,qy3,qz3] = map mkName ["qx3", "qy3", "qz3"]
      patP          = tupP [tupP [varP qx3, varP qy3], listP [varP qz3]]
      patE          = tupE [tupE [varE qx3, varE qy3], listE [varE qz3]]
      cls           = clause ([varP qx3, varP qy3, varP qz3]) (normalB patE) []
      recordPat     = patSynD nm3 (recordPatSyn [qx3,qy3,qz3])
                        (explBidir [cls]) patP

  pats <- sequence [prefixPat, infixPat, recordPat]
  -- pretty print the pattern synonyms:
  mapM_ (runIO . hPutStrLn stderr . pprint) pats
  -- splice in the pattern synonyms
  return pats

{- Test prefix pattern synonyms -}
[d|
 pattern P1 x y z <- ((x,y), [z], _, _)   -- unidirectional pattern
 pattern P2 x y z =  ((x,y), [z])         -- implicit bidirectional pattern
 pattern P3 x y z <- ((x,y), [z]) where   -- explicit bidirectional pattern
   P3 x y z = ((x,y), [z]) |]

{- Test infix pattern synonyms -}
[d|
 pattern x :*: y <- ((x,_), [y])
 pattern x :+: y =  (x,y)
 pattern x :~: y <- (x,y) where
   x :~: y = (x,y) |]

{- Test record pattern synonyms -}
[d|
 pattern R1 {x1, y1} <- ((x1,_), [y1])
 getX1 = x1 ((1, 2), [3]) -- should yield 1
 getY1 = y1 ((1, 2), [3]) -- should yield 3
 pattern R2 {x2, y2} =  (x2, [y2])
 pattern R3 {x3, y3} <- (x3, [y3]) where
   R3 x y = (x, [y]) |]

--x1 = "no, no, no"
--y1 = "no, no, no"

getX1' = x1 ((1, 2), [3]) -- should yield 1
getY1' = y1 ((1, 2), [3]) -- should yield 3

{- Test splicing unidirectional pattern synonyms with different types -}
[d|
 pattern P :: Bool
 pattern P <- True

 pattern Pe :: () => forall a. a -> Ex
 pattern Pe x <- MkEx x

 pattern Pu :: forall a. a -> a
 pattern Pu x <-  x

 pattern Pue :: forall a. () => forall b. a -> b -> (a, Ex)
 pattern Pue x y <- (x, MkEx y)

 pattern Pur :: forall a. (Num a, Eq a) => a -> [a]
 pattern Pur x <- [x, 1]

 pattern Purp :: forall a b. (Num a, Eq a) =>
                 Show b => a -> b -> ([a], UnivProv b)
 pattern Purp x y <- ([x, 1], MkUnivProv y)

 pattern Pure :: forall a. (Num a, Eq a) => forall b. a -> b -> ([a], Ex)
 pattern Pure x y <- ([x, 1], MkEx y)

 pattern Purep :: forall a. (Num a, Eq a) =>
                 forall b. Show b => a -> b -> ([a], ExProv)
 pattern Purep x y <- ([x, 1], MkExProv y)

 pattern Pep :: () => forall a. Show a => a -> ExProv
 pattern Pep x <- MkExProv x

 pattern Pup :: forall a. () => Show a => a -> UnivProv a
 pattern Pup x <- MkUnivProv x

 pattern Puep :: forall a. () => forall b. (Show b) => a -> b -> (ExProv, a)
 pattern Puep x y <- (MkExProv y, x) |]

{- Test reification of different pattern synonyms and their types -}
do
  infos <- mapM reify [ 'P, 'Pe, 'Pu, 'Pue, 'Pur, 'Purp
                      , 'Pure, 'Purep, 'Pep, 'Pup, 'Puep ]
  mapM_ (runIO . hPutStrLn stderr . pprint) infos
    -- NB. use stderr rather than stdout, because GHC does not
    -- guarantee to flush stdout after TH code.  In particular when
    -- the output is going to a file, and we're using GHC with the
    -- runtime linker or with -fexternal-interpreter, stdout will not
    -- get flushed.
  [d| theAnswerIs = 42 |]
