module Unsafe.TrueName (trueName, quasiName) where
import Prelude
#if !MIN_VERSION_base(4,8,0)
import Control.Applicative
#endif
import Data.List (nub)
import Language.Haskell.TH.Quote
import Language.Haskell.TH.Syntax
#if MIN_VERSION_template_haskell(2,8,0)
hiding (trueName)
#endif
conNames :: Con -> [Name]
conNames con = case con of
NormalC name _ -> [name]
RecC name fields -> name : concat
[ fname : typNames typ | (fname, _, typ) <- fields ]
InfixC _ name _ -> [name]
ForallC _ _ con' -> conNames con'
decNames :: Dec -> [Name]
decNames dec = case dec of
FunD _ _ -> []
ValD _ _ _ -> []
DataD _ _ _ cons _ -> conNames =<< cons
NewtypeD _ _ _ con _ -> conNames con
TySynD _ _ typ -> typNames typ
ClassD _ _ _ _ decs -> decNames =<< decs
InstanceD cxt typ decs -> (predNames =<< cxt)
++ typNames typ ++ (decNames =<< decs)
SigD name typ -> name : typNames typ
ForeignD frgn -> case frgn of
ImportF _ _ _ name t -> name : typNames t
ExportF _ _ name t -> name : typNames t
#if MIN_VERSION_template_haskell(2,8,0)
InfixD _ _ -> []
#endif
PragmaD _ -> []
FamilyD _ _ _ _ -> []
DataInstD cxt _ _ cons names -> (conNames =<< cons)
++ names ++ (predNames =<< cxt)
NewtypeInstD cxt _ _ con names -> conNames con
++ names ++ (predNames =<< cxt)
#if !MIN_VERSION_template_haskell(2,9,0)
TySynInstD _ ts t -> (typNames =<< ts) ++ typNames t
#else
TySynInstD _ tse -> tseNames tse
ClosedTypeFamilyD _ _ _ tses -> tseNames =<< tses
RoleAnnotD _ _ -> []
#endif
#if MIN_VERSION_template_haskell(2,10,0)
StandaloneDerivD cxt typ -> (predNames =<< cxt) ++ typNames typ
DefaultSigD _ _ -> []
#endif
#if MIN_VERSION_template_haskell(2,9,0)
tseNames :: TySynEqn -> [Name]
tseNames (TySynEqn ts t) = (typNames =<< ts) ++ typNames t
#endif
predNames :: Pred -> [Name]
#if !MIN_VERSION_template_haskell(2,10,0)
predNames p = case p of
ClassP n ts -> n : (typNames =<< ts)
EqualP s t -> typNames s ++ typNames t
#else
predNames = typNames
#endif
typNames :: Type -> [Name]
typNames typ = case typ of
ForallT _ c t -> (predNames =<< c) ++ typNames t
AppT s t -> typNames s ++ typNames t
SigT t _ -> typNames t
VarT _ -> []
ConT name -> [name]
TupleT _ -> []
UnboxedTupleT _ -> []
ArrowT -> []
ListT -> []
#if MIN_VERSION_template_haskell(2,8,0)
PromotedT _ -> []
PromotedTupleT _ -> []
PromotedNilT -> []
PromotedConsT -> []
StarT -> []
ConstraintT -> []
LitT _ -> []
#endif
#if MIN_VERSION_template_haskell(2,10,0)
EqualityT -> []
#endif
infoNames :: Info -> [Name]
infoNames info = case info of
ClassI dec _ -> decNames dec
ClassOpI _ typ _ _ -> typNames typ
TyConI dec -> decNames dec
FamilyI _ decs -> decNames =<< decs
DataConI _ typ parent _ -> parent : typNames typ
VarI _ typ _ _ -> typNames typ
PrimTyConI _ _ _ -> []
TyVarI _ typ -> typNames typ
trueName :: String -> Name -> Q Name
trueName name thing = do
cons <- nub . infoNames <$> reify thing
case filter (\ n -> name == nameBase n || name == show n) cons of
[n] -> return n
_ -> fail $ "trueName: you wanted " ++ show name ++
", but I have:\n" ++ unlines ((++) "\t" . show <$> cons)
quasiName :: QuasiQuoter
quasiName = QuasiQuoter
{ quoteExp = fmap (ConE . fst) . nameVars
, quotePat = fmap (uncurry ConP) . nameVars
, quoteType = fmap (ConT . fst) . nameVars
, quoteDec = \ _ -> fail "quasiName: I'm not sure how this works."
} where
nameVars spec = do
(name, extra, (things, m'thing)) <- case words spec of
name : s0 : extra -> (,,) name extra <$> case s0 of
'\'' : s1 -> case s1 of
'\'' : s2 -> (,) s2 <$> lookupTypeName s2
_ -> (,) s1 <$> lookupValueName s1
_ -> return (s0, Just $ mkName s0)
_ -> fail $ "quasiName: can't parse spec: " ++ spec
let nope = fail $ "quasiName: not in scope: " ++ things
flip (,) (pat <$> extra) <$> maybe nope (trueName name) m'thing
pat n = case n of
"_" -> WildP
'!' : ns -> BangP (pat ns)
'~' : ns -> TildeP (pat ns)
_ -> VarP (mkName n)